Unity Shader实现水墨效果
Unity Shader学习:水墨效果
偶然在网上看到9级铁甲蛹大神的水墨风格后处理觉得挺有意思,参照着实现一下,还是涉及到之前油画效果的算法,叫什么滤波暂时不清楚,应该用来处理手绘效果挺多的。
水墨风格基本原理:高斯模糊原始图像,用深度算出边缘进行描边,最后用画笔效果的滤波完成最终图像。
有需要可以用Post Proces改变颜色范围,更接近水墨的颜色。
C#部分:
//屏幕后处理基类 using UnityEngine; using System.Collections; //非运行时也触发效果 [ExecuteInEditMode] //屏幕后处理特效一般都需要绑定在摄像机上 [RequireComponent(typeof(Camera))] //提供一个后处理的基类,主要功能在于直接通过Inspector面板拖入shader,生成shader对应的材质 public class PostEffectBase : MonoBehaviour { //Inspector面板上直接拖入 public Shader shader = null; private Material _material = null; public Material _Material { get { if (_material == null) _material = GenerateMaterial(shader); return _material; } } //根据shader创建用于屏幕特效的材质 protected Material GenerateMaterial(Shader shader) { if (shader == null) return null; //需要判断shader是否支持 if (shader.isSupported == false) return null; Material material = new Material(shader); material.hideFlags = HideFlags.DontSave; if (material) return material; return null; } }
//挂在摄像机上 using System.Collections; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] public class ChineseInkPostEffect : PostEffectBase { /// <summary> /// 降分辨率未操作 /// </summary> [Range(0,5)] public int downSample = 1; /// <summary> /// 高斯模糊采样缩放系数 /// </summary> [Range(0,5)] public int samplerScale = 1; /// <summary> /// 高斯模糊迭代次数 /// </summary> [Range(0,10)] public int count = 1; /// <summary> /// 边缘宽度 /// </summary> [Range(0.0f,10.0f)] public float edgeWidth = 3.0f; /// <summary> /// 边缘最小宽度 /// </summary> [Range(0.0f,1.0f)] public float sensitive = 0.35f; /// <summary> /// 画笔滤波系数 /// </summary> [Range(0,10)] public int paintFactor = 4; /// <summary> /// 噪声图 /// </summary> public Texture noiseTexture; private Camera cam; private void Start() { cam = GetComponent<Camera>(); //开启深度法线图 cam.depthTextureMode = DepthTextureMode.DepthNormals; } private void OnRenderImage(RenderTexture source, RenderTexture destination) { if (_Material) { RenderTexture temp1 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0, source.format); RenderTexture temp2 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0, source.format); Graphics.Blit(source, temp1); for (int i = 0; i < count; i++) { //高斯模糊横向纵向两次(pass0) _Material.SetVector("_offsets", new Vector4(0, samplerScale, 0, 0)); Graphics.Blit(temp1, temp2, _Material, 0); _Material.SetVector("_offsets", new Vector4(samplerScale, 0, 0, 0)); Graphics.Blit(temp2, temp1, _Material, 0); } //描边(pass1) _Material.SetTexture("_BlurTex", temp1); _Material.SetTexture("_NoiseTex", noiseTexture); _Material.SetFloat("_EdgeWidth", edgeWidth); _Material.SetFloat("_Sensitive", sensitive); Graphics.Blit(temp1, temp2,_Material,1); //画笔滤波(pass2) _Material.SetTexture("_PaintTex", temp2); _Material.SetInt("_PaintFactor", paintFactor); Graphics.Blit(temp2, destination, _Material, 2); RenderTexture.ReleaseTemporary(temp1); RenderTexture.ReleaseTemporary(temp2); } } }
shader部分:
Shader "Unlit/ChineseInk" { Properties { //原始画面 _MainTex ("Texture", 2D) = "white" {} //高斯模糊画面 _BlurTex("Blur",2D) = "white"{} //水墨画面 _PaintTex("PaintTex",2D)="white"{} } CGINCLUDE #include "UnityCG.cginc" //深度法线图 sampler2D _CameraDepthNormalsTexture; sampler2D _MainTex; sampler2D _BlurTex; sampler2D _PaintTex; sampler2D _NoiseTex; float4 _BlurTex_TexelSize; float4 _MainTex_ST; float4 _MainTex_TexelSize; float4 _PaintTex_TexelSize; float4 _offsets; float _EdgeWidth; float _Sensitive; int _PaintFactor; //取灰度 float luminance(fixed3 color) { return 0.2125*color.r + 0.7154*color.g + 0.0721*color.b; } //高斯模糊部分 struct v2f_blur { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float4 uv01:TEXCOORD1; float4 uv23:TEXCOORD2; float4 uv45:TEXCOORD3; }; v2f_blur vert_blur(appdata_img v) { v2f_blur o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord.xy; _offsets *= _MainTex_TexelSize.xyxy; o.uv01 = v.texcoord.xyxy + _offsets.xyxy*float4(1, 1, -1, -1); o.uv23 = v.texcoord.xyxy + _offsets.xyxy*float4(1, 1, -1, -1)*2.0; o.uv45 = v.texcoord.xyxy + _offsets.xyxy*float4(1, 1, -1, -1)*3.0; return o; } float4 frag_blur(v2f_blur i) : SV_Target { float4 color = float4(0,0,0,0); color += 0.40*tex2D(_MainTex, i.uv); color += 0.15*tex2D(_MainTex, i.uv01.xy); color += 0.15*tex2D(_MainTex, i.uv01.zw); color += 0.10*tex2D(_MainTex, i.uv23.xy); color += 0.10*tex2D(_MainTex, i.uv23.zw); color += 0.05*tex2D(_MainTex, i.uv45.xy); color += 0.05*tex2D(_MainTex, i.uv45.zw); return color; } //边缘检测部分 struct v2f_edge{ float2 uv:TEXCOORD0; float4 vertex:SV_POSITION; }; v2f_edge vert_edge(appdata_img v){ v2f_edge o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } float4 frag_edge(v2f_edge i):SV_Target{ //噪声 float n = tex2D(_NoiseTex,i.uv).r; float3 col0 = tex2D(_CameraDepthNormalsTexture, i.uv + _EdgeWidth * _BlurTex_TexelSize.xy*float2(1,1)).xyz; float3 col1 = tex2D(_CameraDepthNormalsTexture, i.uv + _EdgeWidth * _BlurTex_TexelSize.xy*float2(1,-1)).xyz; float3 col2 = tex2D(_CameraDepthNormalsTexture, i.uv + _EdgeWidth * _BlurTex_TexelSize.xy*float2(-1, 1)).xyz; float3 col3 = tex2D(_CameraDepthNormalsTexture, i.uv + _EdgeWidth * _BlurTex_TexelSize.xy*float2(-1,-1)).xyz; float edge = luminance(pow(col0 - col3, 2) + pow(col1 - col2, 2)); edge = pow(edge, 0.2); if (edge<_Sensitive) { edge = 0; } else { edge = n; } float3 color = tex2D(_BlurTex, i.uv); float3 finalColor = edge * float3(0, 0, 0) + (1 - edge)*color*(0.95+0.1*n); return float4(finalColor, 1.0); } //画笔滤波部分 struct v2f_paint { float2 uv:TEXCOORD0; float4 vertex:SV_POSITION; }; v2f_paint vert_paint(appdata_img v) { v2f_paint o; o.uv = v.texcoord; o.vertex = UnityObjectToClipPos(v.vertex); return o; } float4 frag_paint(v2f_paint i):SV_Target{ float3 m0 = 0.0; float3 m1 = 0.0; float3 s0 = 0.0; float3 s1 = 0.0; float3 c = 0.0; int radius = _PaintFactor; int r = (radius + 1)*(radius + 1); for (int j = -radius; j <= 0; ++j) { for (int k = -radius; k <= 0; ++k) { c = tex2D(_PaintTex, i.uv + _PaintTex_TexelSize.xy * float2(k, j)).xyz; m0 += c; s0 += c * c; } } for (int j = 0; j <= radius; ++j) { for (int k = 0; k <= radius; ++k) { c = tex2D(_PaintTex, i.uv + _PaintTex_TexelSize.xy * float2(k, j)).xyz; m1 += c; s1 += c * c; } } float4 finalFragColor = 0.; float min_sigma2 = 1e+2; m0 /= r; s0 = abs(s0 / r - m0 * m0); float sigma2 = s0.r + s0.g + s0.b; if (sigma2 < min_sigma2) { min_sigma2 = sigma2; finalFragColor = float4(m0, 1.0); } m1 /= r; s1 = abs(s1 / r - m1 * m1); sigma2 = s1.r + s1.g + s1.b; if (sigma2 < min_sigma2) { min_sigma2 = sigma2; finalFragColor = float4(m1, 1.0); } return finalFragColor; } ENDCG SubShader { Pass { CGPROGRAM #pragma vertex vert_blur #pragma fragment frag_blur ENDCG } Pass { CGPROGRAM #pragma vertex vert_edge #pragma fragment frag_edge ENDCG } Pass { CGPROGRAM #pragma vertex vert_paint #pragma fragment frag_paint ENDCG } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
赞 (0)