Unity Shader 屏幕后效果——高斯模糊
高斯模糊是圖像模糊處理中非常經(jīng)典和常見(jiàn)的一種算法,也是Bloom屏幕效果的基礎(chǔ)。
?
實(shí)現(xiàn)高斯模糊同樣用到了卷積的概念,關(guān)于卷積的概念和原理詳見(jiàn)我的另一篇博客:
?https://www.cnblogs.com/koshio0219/p/11137155.html
?
通過(guò)高斯方程計(jì)算出的卷積核稱為高斯核,一個(gè)5*5的高斯核對(duì)它進(jìn)行權(quán)重歸一化如下:
| 0.0030 | 0.0133 | 0.0219 | 0.0133 | 0.0030 |
| 0.0133 | 0.0596 | 0.0983 | 0.0596 | 0.0133 |
| 0.0219 | 0.0983 | 0.1621 | 0.0983 | 0.0219 |
| 0.0133 | 0.0596 | 0.0983 | 0.0596 | 0.0133 |
| 0.0030 | 0.0133 | 0.0219 | 0.0133 | 0.0030 |
?
?
?
?
?
?
?
通過(guò)表也可以很清楚的看到,離原點(diǎn)越近的點(diǎn)模糊程度影響越大,反之越小。
為了優(yōu)化計(jì)算,可以將這個(gè)5*5矩陣簡(jiǎn)化為兩個(gè)矩陣分別計(jì)算,得到的效果是相同的。
它們分別是一個(gè)1*5的橫向矩陣和一個(gè)5*1的縱向矩陣,這樣我們只需要對(duì)橫縱向矩陣分別進(jìn)行一次采樣既可,這樣可以很大程度的減少計(jì)算量。
拆分之后結(jié)果如下:
?
?我們發(fā)現(xiàn),最終的計(jì)算只需要記錄3個(gè)權(quán)重值既可,它們是weight[3]={0.4026,0.2442,0.0545};
?
具體實(shí)現(xiàn):
1.實(shí)現(xiàn)調(diào)整高斯模糊參數(shù)的腳本。
為了進(jìn)一步優(yōu)化計(jì)算,這里加入了降采樣系數(shù),模糊范圍縮放;為此,需要在外部增加模糊采樣的迭代次數(shù),具體如下:
?
1 using UnityEngine; 2 3 public class GaussianBlurCtrl : ScreenEffectBase 4 { 5 private const string _BlurSize = "_BlurSize";//只有模糊范圍需要在GPU中計(jì)算 6 7 [Range(0, 4)] 8 public int iterations = 3;//迭代次數(shù) 9 [Range(0.2f, 3)] 10 public float blurSize = 0.6f;//模糊范圍 11 [Range(1, 8)] 12 public int downSample = 2;//降采樣系數(shù) 13 14 private void OnRenderImage(RenderTexture source, RenderTexture destination) 15 { 16 if (Material != null) 17 { 18 //得到屏幕的渲染紋理后直接除以降采樣系數(shù)以成倍減少計(jì)算量,但過(guò)大時(shí)模糊效果不佳 19 int rtw = source.width/downSample; 20 int rth = source.height/downSample; 21 22 RenderTexture buffer0 = RenderTexture.GetTemporary(rtw, rth, 0); 23 buffer0.filterMode = FilterMode.Bilinear; 24 25 Graphics.Blit(source, buffer0); 26 27 //利用迭代次數(shù)對(duì)模糊范圍加以控制,用到了類似于雙緩沖的方式對(duì)紋理進(jìn)行處理 28 for (int i = 0; i < iterations; i++) 29 { 30 //設(shè)置采樣范圍,根據(jù)迭代次數(shù)范圍增加,之后會(huì)與紋理坐標(biāo)進(jìn)行乘積操作,固基礎(chǔ)值為1 31 Material.SetFloat(_BlurSize, blurSize*i+1); 32 33 RenderTexture buffer1 = RenderTexture.GetTemporary(rtw, rth, 0); 34 Graphics.Blit(buffer0, buffer1, Material, 0); 35 //每次處理完立即釋放相應(yīng)緩存,因?yàn)閁nity內(nèi)部已經(jīng)對(duì)此做了相應(yīng)的優(yōu)化 36 RenderTexture.ReleaseTemporary(buffer0); 37 buffer0 = RenderTexture.GetTemporary(rtw, rth, 0); 38 Graphics.Blit(buffer1, buffer0,Material, 1); 39 RenderTexture.ReleaseTemporary(buffer1); 40 } 41 Graphics.Blit(buffer0, destination); 42 RenderTexture.ReleaseTemporary(buffer0); 43 } 44 else 45 Graphics.Blit(source, destination); 46 } 47 }基類腳本見(jiàn):
https://www.cnblogs.com/koshio0219/p/11131619.html
?
2.在Shader中分別進(jìn)行橫向和縱向的模糊計(jì)算,分為兩個(gè)Pass進(jìn)行,具體如下:
1 Shader "MyUnlit/GaussianBlur" 2 { 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 } 7 SubShader 8 { 9 Tags { "RenderType"="Opaque" } 10 11 //CGINCLUDE中的代碼可被其他Pass重復(fù)調(diào)用,用于簡(jiǎn)化不必要的重復(fù)代碼 12 CGINCLUDE 13 14 #pragma multi_compile_fog 15 #include "UnityCG.cginc" 16 17 struct appdata 18 { 19 float4 vertex : POSITION; 20 float2 uv : TEXCOORD0; 21 }; 22 23 struct v2f 24 { 25 half2 uv[5] : TEXCOORD0; 26 UNITY_FOG_COORDS(1) 27 float4 pos : SV_POSITION; 28 }; 29 30 sampler2D _MainTex; 31 float4 _MainTex_TexelSize; 32 float _BlurSize; 33 34 //用于計(jì)算縱向模糊的紋理坐標(biāo)元素 35 v2f vert_v(appdata v) 36 { 37 v2f o; 38 o.pos = UnityObjectToClipPos(v.vertex); 39 half2 uv = v.uv; 40 41 //以擴(kuò)散的方式對(duì)數(shù)組進(jìn)行排序,只偏移y軸,其中1和2,3和4分別位于原始點(diǎn)0的上下,且距離1個(gè)單位和2個(gè)像素單位 42 //得到的最終偏移與模糊范圍的控制參數(shù)進(jìn)行乘積 43 o.uv[0] = uv; 44 o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y*1.0)*_BlurSize; 45 o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y*1.0)*_BlurSize; 46 o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y*2.0)*_BlurSize; 47 o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y*2.0)*_BlurSize; 48 49 UNITY_TRANSFER_FOG(o, o.vertex); 50 return o; 51 } 52 53 //用于計(jì)算橫向模糊的紋理坐標(biāo)元素 54 v2f vert_h(appdata v) 55 { 56 v2f o; 57 o.pos = UnityObjectToClipPos(v.vertex); 58 half2 uv = v.uv; 59 60 //與上面同理,只不過(guò)是x軸向的模糊偏移 61 o.uv[0] = uv; 62 o.uv[1] = uv + float2( _MainTex_TexelSize.x*1.0,0.0)*_BlurSize; 63 o.uv[2] = uv - float2( _MainTex_TexelSize.x*1.0,0.0)*_BlurSize; 64 o.uv[3] = uv + float2( _MainTex_TexelSize.x*2.0,0.0)*_BlurSize; 65 o.uv[4] = uv - float2( _MainTex_TexelSize.x*2.0,0.0)*_BlurSize; 66 67 UNITY_TRANSFER_FOG(o, o.vertex); 68 return o; 69 } 70 71 //在片元著色器中進(jìn)行最終的模糊計(jì)算,此過(guò)程在每個(gè)Pass中都會(huì)進(jìn)行一次計(jì)算,但計(jì)算方式是統(tǒng)一的 72 fixed4 frag(v2f i) : SV_Target 73 { 74 float weights[3] = {0.4026,0.2442,0.0545}; 75 76 fixed4 col = tex2D(_MainTex, i.uv[0]); 77 78 fixed3 sum = col.rgb*weights[0]; 79 80 //對(duì)采樣結(jié)果進(jìn)行對(duì)應(yīng)紋理偏移坐標(biāo)的權(quán)重計(jì)算,以得到模糊的效果 81 for (int it = 1; it < 3; it++) 82 { 83 sum += tex2D(_MainTex, i.uv[2 * it - 1]).rgb*weights[it];//對(duì)應(yīng)1和3,也就是原始像素的上方兩像素 84 sum += tex2D(_MainTex, i.uv[2 * it]).rgb*weights[it];//對(duì)應(yīng)2和4,下方兩像素 85 } 86 fixed4 color = fixed4(sum, 1.0); 87 UNITY_APPLY_FOG(i.fogCoord, color); 88 return color; 89 } 90 91 ENDCG 92 93 ZTest Always 94 Cull Off 95 ZWrite Off 96 97 //縱向模糊Pass,直接用指令調(diào)用上面的函數(shù) 98 Pass 99 { 100 NAME "GAUSSIANBLUR_V" 101 CGPROGRAM 102 #pragma vertex vert_v 103 #pragma fragment frag 104 105 ENDCG 106 } 107 108 //橫向模糊Pass 109 Pass 110 { 111 NAME "GAUSSIANBLUR_H" 112 CGPROGRAM 113 #pragma vertex vert_h 114 #pragma fragment frag 115 116 ENDCG 117 } 118 } 119 Fallback Off 120 }效果如下:
轉(zhuǎn)載于:https://www.cnblogs.com/koshio0219/p/11152534.html
總結(jié)
以上是生活随笔為你收集整理的Unity Shader 屏幕后效果——高斯模糊的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 创建 Npm+webpack
- 下一篇: 想成为嵌入式程序员应知道的16个基本问题