Unity Shader入门教程(十七):均值模糊与高斯模糊
发表于2018-06-07
一、均值模糊






1、简单一次均值
(1)代码实践:
Shader "Custom/Edu/SimpleBlur" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _BlurRadius("Blur Radius",Range(0,30)) = 5 } CGINCLUDE #include "UnityCG.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; float4 _MainTex_TexelSize; half _BlurRadius; struct a2v { float4 vertex:POSITION; float2 texcoord:TEXCOORD; }; struct v2f { float4 pos:SV_POSITION; half2 uv:TEXCOORD0; half2 uv1:TEXCOORD1; half2 uv2:TEXCOORD2; half2 uv3:TEXCOORD3; half2 uv4:TEXCOORD4; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv = v.texcoord; o.uv1 = v.texcoord + half2(1,1) * _MainTex_TexelSize *_BlurRadius ; o.uv2 = v.texcoord + half2(-1,1) * _MainTex_TexelSize *_BlurRadius ; o.uv3 = v.texcoord + half2(1,-1) * _MainTex_TexelSize *_BlurRadius ; o.uv4 = v.texcoord + half2(-1,-1) * _MainTex_TexelSize *_BlurRadius ; return o; } fixed4 frag(v2f i):SV_Target { fixed4 color; color = tex2D(_MainTex,i.uv) * _Color; color += tex2D(_MainTex,i.uv1) * _Color; color += tex2D(_MainTex,i.uv2) * _Color; color += tex2D(_MainTex,i.uv3) * _Color; color += tex2D(_MainTex,i.uv4) * _Color; return color*0.2; } ENDCG SubShader { Pass { Tags{"LightMode" = "ForwardBase"} ZTest Off Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } FallBack "Diffuse" }
(2)效果图:

2、简单一次均值增加采样点
(1)代码实践:
Shader "Custom/Edu/SimpleBlur" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _BlurRadius("Blur Radius",Range(-20,20)) = 5 } CGINCLUDE #include "UnityCG.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; float4 _MainTex_TexelSize; half _BlurRadius; struct a2v { float4 vertex:POSITION; float2 texcoord:TEXCOORD; }; struct v2f { float4 pos:SV_POSITION; half2 uv:TEXCOORD0; half4 uv1:TEXCOORD1; half4 uv2:TEXCOORD2; half4 uv3:TEXCOORD3; half4 uv4:TEXCOORD4; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv = v.texcoord; o.uv1.xy = v.texcoord + half2(1,1) * _MainTex_TexelSize *_BlurRadius ; o.uv2.xy = v.texcoord + half2(-1,1) * _MainTex_TexelSize *_BlurRadius ; o.uv3.xy = v.texcoord + half2(1,-1) * _MainTex_TexelSize *_BlurRadius ; o.uv4.xy = v.texcoord + half2(-1,-1) * _MainTex_TexelSize *_BlurRadius ; o.uv1.zw = v.texcoord + half2(1,0) * _MainTex_TexelSize *_BlurRadius ; o.uv2.zw = v.texcoord + half2(-1,0) * _MainTex_TexelSize *_BlurRadius ; o.uv3.zw = v.texcoord + half2(0,-1) * _MainTex_TexelSize *_BlurRadius ; o.uv4.zw = v.texcoord + half2(0,1) * _MainTex_TexelSize *_BlurRadius ; return o; } fixed4 frag(v2f i):SV_Target { fixed4 color; color = tex2D(_MainTex,i.uv) * _Color; color += color; color += tex2D(_MainTex,i.uv1.xy) * _Color; color += tex2D(_MainTex,i.uv1.zw) * _Color; color += tex2D(_MainTex,i.uv2.xy) * _Color; color += tex2D(_MainTex,i.uv2.zw) * _Color; color += tex2D(_MainTex,i.uv3.xy) * _Color; color += tex2D(_MainTex,i.uv3.zw) * _Color; color += tex2D(_MainTex,i.uv4.xy) * _Color; color += tex2D(_MainTex,i.uv4.zw) * _Color; return color*0.1; } ENDCG SubShader { Pass { Tags{"LightMode" = "ForwardBase"} ZTest Off Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } FallBack "Diffuse" }
(2)效果图

3、屏幕后期处理多次迭代均值模糊
(1)基本思路:
在摄像机的OnRenderImage函数中,声明一个RenderTexture,buffer0,先将src复制到buffer0。
声明buffer0、buffer1时其分辨率可以通过降采样参数downSample来控制。
对图像进行降采样不仅可以减少需要处理的像素个数,提高性能,而且适当降采样可以得到更好的模糊效果。
然后在For循环中将buffer0作为_MainTex交给指定shader的指定pass进行处理。
中间会用到临时的buffer1,每次buffer0被赋予新值前要先调用RenderTexture的ReleaseTemporary静态方法先将其释放。最后将buffer0复制到dest。
(2)代码实践:
using UnityEngine; using System.Collections; using System; using UnityEngine.UI; using System.Security.Cryptography; [ExecuteInEditMode] public class SimpleBlur : PostEffectsBase { public Shader simpleBlurShader; private Material simpleBlurMat = null; public Material material { get { simpleBlurMat = CheckShaderAndCreateMaterial (simpleBlurShader, simpleBlurMat); return simpleBlurMat; } } [Range(0,4)] public int iterations = 3; [Range(0.2f,0.5f)] public float blurSpread = 0.6f; [Range(1,8)] public int downSample = 2; void OnRenderImage(RenderTexture src,RenderTexture dest) { if (material != null) { int rtH = src.width / downSample; int rtW = src.height / downSample; RenderTexture buffer0 = RenderTexture.GetTemporary (rtW, rtH, 0); //设置滤波模式为二次线性滤波 buffer0.filterMode = FilterMode.Bilinear; //先把src渲染到buffer0上!!!! Graphics.Blit (src, buffer0); for (int i = 0; i < iterations; i++) { RenderTexture buffer1 = RenderTexture.GetTemporary (rtH, rtW, 0); Graphics.Blit (buffer0, buffer1, material, 0); //使用该静态方法释放临时RT的内容 //为buffer0赋予新的渲染内容前必须想将其释放 RenderTexture.ReleaseTemporary (buffer0); buffer0 = buffer1; } Graphics.Blit (buffer0, dest); RenderTexture.ReleaseTemporary (buffer0); } else { Graphics.Blit (src, dest); } }
(3)效果图:


二、高斯模糊
(1)简介
均值模糊由于采样次数较少,每个像素以及其周围像素的权值是相同的,模糊出来的效果不佳,而多次迭代处理虽然可以增强模糊效果,但是迭代大大地增加了性能的消耗,虽然在学习时可以用迭代来达到效果,但是要实际使用的时候,效率就不得不成为我们考虑的重要因素。
高斯模糊就是在采样迭代的过程中在片元着色器内考虑各个采样点距离中心的远近,并乘以符合高斯分布的权重值。
高斯分布

(2)代码实践
CS代码
using UnityEngine; using System.Collections; using System.Text.RegularExpressions; using System.Security.Cryptography; public class MyGaussianBlur : PostEffectsBase { public Shader GaussianBlurShader; private Material GaussianBlurMat; public Material mat { get { if (GaussianBlurMat == null) { GaussianBlurMat = CheckShaderAndCreateMaterial (GaussianBlurShader, GaussianBlurMat); } return GaussianBlurMat; } } [Range(0,8)] public int downSample; [Range(0,4)] public int iterations; [Range(0,10)] public float blurRadius; void OnRenderImage(RenderTexture src,RenderTexture dest) { if (mat != null) { mat.SetFloat ("_BlurRadius", blurRadius); int rtW = src.width / downSample; int rtH = src.height / downSample; //最后一个参数depth,当为0时,表示不产生深度Z的缓冲 RenderTexture buffer0 = RenderTexture.GetTemporary (rtW, rtH, 0); buffer0.filterMode = FilterMode.Bilinear; Graphics.Blit (src, buffer0); for (int i = 0; i < iterations; i++) { //pass 0 ,在水平方向上进行模糊 RenderTexture buffer1 = RenderTexture.GetTemporary (rtW, rtH, 0); Graphics.Blit (buffer0, buffer1, mat, 0); RenderTexture.ReleaseTemporary (buffer0); buffer0 = buffer1; buffer1 = RenderTexture.GetTemporary (rtW, rtH, 0); Graphics.Blit (buffer0, buffer1, mat, 1); RenderTexture.ReleaseTemporary (buffer0); buffer0 = buffer1; //RenderTexture.ReleaseTemporary (buffer1); } Graphics.Blit (buffer0, dest); RenderTexture.ReleaseTemporary (buffer0); } else { Graphics.Blit (src, dest); } } }
Shader代码
Shader "Custom/Edu/GaussianBlur" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _BlurRadius("Blur Radius",Range(0,10)) = 5 _Color("Color Tint",Color)= (1,1,1,1) } CGINCLUDE #include "unityCG.cginc" sampler2D _MainTex; float4 _MainTex_TexelSize; half _BlurRadius; struct a2v { float4 vertex:POSTION; float2 texcoord:TEXCOORD0; }; struct v2f { float4 pos:SV_POSITION; half2 uv[5]:TEXCOORD0; }; v2f vertBlurVertical(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv[0] = v.texcoord ; o.uv[1] = v.texcoord + _BlurRadius * float2(0,1) * _MainTex_TexelSize; o.uv[2] = v.texcoord + _BlurRadius * float2(0,-1) * _MainTex_TexelSize; o.uv[3] = v.texcoord + _BlurRadius * float2(0,2) * _MainTex_TexelSize; o.uv[4] = v.texcoord + _BlurRadius * float2(0,-2) * _MainTex_TexelSize; return o; } v2f vertBlurHorizontal(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv[0] = v.texcoord ; o.uv[1] = v.texcoord + _BlurRadius * float2(1,0) * _MainTex_TexelSize; o.uv[2] = v.texcoord + _BlurRadius * float2(-1,0) * _MainTex_TexelSize; o.uv[3] = v.texcoord + _BlurRadius * float2(2,0) * _MainTex_TexelSize; o.uv[4] = v.texcoord + _BlurRadius * float2(-2,0) * _MainTex_TexelSize; return o; } fixed4 fragBlur(v2f i):SV_Target { float weight[3] = {0.4026,0.2442,0.0545}; fixed3 sum = tex2D(_MainTex,i.uv[0]).rgb * weight[0]; // 为什么不用i呢?因为传入的v2f变量实参的名称就是 i ! for(int it=1; it<3; it++){ sum += tex2D(_MainTex, i.uv[2*it]).rgb * weight[it]; sum += tex2D(_MainTex, i.uv[2*it-1]).rgb * weight[it]; } return fixed4(sum,1.0); } ENDCG SubShader { Tags { "RenderType"="Opaque" } ZTest Always Cull Off ZWrite Off Pass { //潜意识中的错误写法,不需要等号 //NAME = "vertBlurVertical" NAME "vertBlurVertical" CGPROGRAM #pragma vertex vertBlurVertical #pragma fragment fragBlur ENDCG } Pass { NAME "vertBlurHorizontal" CGPROGRAM #pragma vertex vertBlurHorizontal #pragma fragment fragBlur ENDCG } } FallBack "Diffuse" }
(3) 效果图

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引