Unity Shader:Blur


花了一晚上的時間終於看懂Image Effect中的Blur,其實很簡單,就是一下子沒有理解到。

原理:使用兩個一維[1*7]的高斯濾波模板,一個用在x方向,另一個用在y方向。高斯濾波有模糊的效果。

js腳本參數:

Down Sample:OnRenderImage中獲取的圖像進行降采樣,其實就是把要處理的紋理變小。有利於加快shader運行速度。

Blur Size:在使用高斯模板時,相鄰像素點的間隔。越大間隔越遠,圖像越模糊。但過大的值會導致失真。

Blur Iterations:迭代次數,越大模糊效果越好,但消耗越大。

Blur Type:兩個不同的shader,后一個是前一個的優化版本,但差別不大。

 

具體代碼分析:

function OnRenderImage (source : RenderTexture, destination : RenderTexture) {    
        if(CheckResources() == false) {
            Graphics.Blit (source, destination);
            return;
        }

        var widthMod : float = 1.0f / (1.0f * (1<<downsample)); // 降采樣系數的倒數,用於調整降采樣后,相鄰像素的間隔

        // blurMaterial.SetVector ("_Parameter", Vector4 (blurSize * widthMod, -blurSize * widthMod, 0.0f, 0.0f));
        source.filterMode = FilterMode.Bilinear;

        var rtW : int = source.width >> downsample; // >> 是除法的優化
        var rtH : int = source.height >> downsample;

        // downsample
        var rt : RenderTexture = RenderTexture.GetTemporary (rtW, rtH, 0, source.format);

        rt.filterMode = FilterMode.Bilinear;
      // 對應的shader的Pass 0 Graphics.Blit (source, rt, blurMaterial,
0); //首先對圖像進行降采樣,同時進行簡單的模糊 var passOffs = blurType == BlurType.StandardGauss ? 0 : 2; // 選擇不同的blurtype,就調用不同的shader pass for(var i : int = 0; i < blurIterations; i++) { var iterationOffs : float = (i*1.0f);
        // _Parameter.x 記錄的是 相鄰像素的間隔,隨着迭代次數增大 blurMaterial.SetVector (
"_Parameter", Vector4 (blurSize * widthMod + iterationOffs, -blurSize * widthMod - iterationOffs, 0.0f, 0.0f)); // vertical blur 垂直濾波 var rt2 : RenderTexture = RenderTexture.GetTemporary (rtW, rtH, 0, source.format); rt2.filterMode = FilterMode.Bilinear; Graphics.Blit (rt, rt2, blurMaterial, 1 + passOffs); // 對應着shader的pass 1,2 RenderTexture.ReleaseTemporary (rt); rt = rt2; // horizontal blur 水平濾波 rt2 = RenderTexture.GetTemporary (rtW, rtH, 0, source.format); rt2.filterMode = FilterMode.Bilinear; Graphics.Blit (rt, rt2, blurMaterial, 2 + passOffs); // 對應着shader的pass 3,4 RenderTexture.ReleaseTemporary (rt); rt = rt2; } Graphics.Blit (rt, destination); RenderTexture.ReleaseTemporary (rt); }

接着分析shader文件:

先看5個pass,分別是用在上文cs腳本中的Bilt函數中。

SubShader {
      ZTest Off Cull Off ZWrite Off Blend Off
      Fog { Mode off }  

    // 0 
    Pass { 
    
        CGPROGRAM
        
        #pragma vertex vert4Tap 
        #pragma fragment fragDownsample
        #pragma fragmentoption ARB_precision_hint_fastest 
        
        ENDCG
         
        }

    // 1
    Pass {
        ZTest Always
        Cull Off
        
        CGPROGRAM 
        
        #pragma vertex vertBlurVertical
        #pragma fragment fragBlur8
        #pragma fragmentoption ARB_precision_hint_fastest 
        
        ENDCG 
        }    
        
    // 2
    Pass {        
        ZTest Always
        Cull Off
                
        CGPROGRAM
        
        #pragma vertex vertBlurHorizontal
        #pragma fragment fragBlur8
        #pragma fragmentoption ARB_precision_hint_fastest 
        
        ENDCG
        }    

    // alternate blur
    // 3
    Pass {
        ZTest Always
        Cull Off
        
        CGPROGRAM 
        
        #pragma vertex vertBlurVerticalSGX
        #pragma fragment fragBlurSGX
        #pragma fragmentoption ARB_precision_hint_fastest 
        
        ENDCG
        }    
        
    // 4
    Pass {        
        ZTest Always
        Cull Off
                
        CGPROGRAM
        
        #pragma vertex vertBlurHorizontalSGX
        #pragma fragment fragBlurSGX
        #pragma fragmentoption ARB_precision_hint_fastest 
        
        ENDCG
        }    
    }    

pass 0:在降采樣的同時,進行簡單地模糊處理。

v2f_tap vert4Tap ( appdata_img v )
        {
            v2f_tap o;

            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
        // 取像素周圍的點 o.uv20
= v.texcoord + _MainTex_TexelSize.xy; o.uv21 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h,-0.5h); o.uv22 = v.texcoord + _MainTex_TexelSize.xy * half2(0.5h,-0.5h); o.uv23 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h,0.5h); return o; } fixed4 fragDownsample ( v2f_tap i ) : SV_Target { fixed4 color = tex2D (_MainTex, i.uv20); color += tex2D (_MainTex, i.uv21); color += tex2D (_MainTex, i.uv22); color += tex2D (_MainTex, i.uv23); return color / 4; }

接下來的pass 1,2 和pass 3, 4,都是分別在x y兩個方向進行高斯濾波。

先看看高斯濾波模板:

static const half4 curve4[7] = { half4(0.0205,0.0205,0.0205,0), half4(0.0855,0.0855,0.0855,0), half4(0.232,0.232,0.232,0),
            half4(0.324,0.324,0.324,1), half4(0.232,0.232,0.232,0), half4(0.0855,0.0855,0.0855,0), half4(0.0205,0.0205,0.0205,0) };

這是 [1*7]的模板,對中間點像素的左右兩邊各3個像素,總共7個像素進行加權求和,得到新的像素值。

pass 1,2的只有vert函數不一樣,分別是取水平和垂直方向的偏差值。

v2f_withBlurCoords8 vertBlurHorizontal (appdata_img v)
        {
            v2f_withBlurCoords8 o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            
            o.uv = half4(v.texcoord.xy,1,1);
            o.offs = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _Parameter.x; // 水平方向的偏差值

            return o; 
        }
        
        v2f_withBlurCoords8 vertBlurVertical (appdata_img v)
        {
            v2f_withBlurCoords8 o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            
            o.uv = half4(v.texcoord.xy,1,1);
            o.offs = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _Parameter.x; // 垂直方向的偏差值
             
            return o; 
        }    

        half4 fragBlur8 ( v2f_withBlurCoords8 i ) : SV_Target
        {
            half2 uv = i.uv.xy; 
            half2 netFilterWidth = i.offs;  
            half2 coords = uv - netFilterWidth * 3.0;  // 這里從中心點偏移3個間隔,從最左邊或者是最上邊開始進行加權累加
            
            half4 color = 0;
              for( int l = 0; l < 7; l++ )  
              {   
                half4 tap = tex2D(_MainTex, coords);
                color += tap * curve4[l]; // 像素值乘上對應的權值
                coords += netFilterWidth; // 移到下一個像素
              }
            return color;
        }

在pass 1,2中的uv值都是float2向量,然而寄存器可以一次性儲存float4,即可以一個float4值存儲兩個uv值。並且像素着色器函數中,計算相鄰像素的步驟,可以放在頂點着色器中。於是就有下面這個版本:

        v2f_withBlurCoordsSGX vertBlurHorizontalSGX (appdata_img v)
        {
            v2f_withBlurCoordsSGX o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            
            o.uv = v.texcoord.xy;
            half2 netFilterWidth = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _Parameter.x; 
            half4 coords = -netFilterWidth.xyxy * 3.0;
            // 計算左右相鄰各3個像素的坐標
            o.offs[0] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
            coords += netFilterWidth.xyxy;
            o.offs[1] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
            coords += netFilterWidth.xyxy;
            o.offs[2] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);

            return o; 
        }        
        
        v2f_withBlurCoordsSGX vertBlurVerticalSGX (appdata_img v)
        {
            v2f_withBlurCoordsSGX o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            
            o.uv = half4(v.texcoord.xy,1,1);
            half2 netFilterWidth = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _Parameter.x;
            half4 coords = -netFilterWidth.xyxy * 3.0;
            // 計算上下相鄰各3個像素的坐標
            o.offs[0] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
            coords += netFilterWidth.xyxy;
            o.offs[1] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
            coords += netFilterWidth.xyxy;
            o.offs[2] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);

            return o; 
        }    

        half4 fragBlurSGX ( v2f_withBlurCoordsSGX i ) : SV_Target
        {
            half2 uv = i.uv.xy;
            
            half4 color = tex2D(_MainTex, i.uv) * curve4[3]; // 中間像素,乘上對應的權值
            
              for( int l = 0; l < 3; l++ )  
              {   
                half4 tapA = tex2D(_MainTex, i.offs[l].xy);
                half4 tapB = tex2D(_MainTex, i.offs[l].zw); 
                color += (tapA + tapB) * curve4[l]; // 由於模板是對稱的,可以使用相同的權值
              }

            return color;

        }

結論:

通過調試,發現使用downsampler為1,iteration為2時,調整blursize可以得到較好的效果,並且性能較好。但blursize為0時,還是模糊圖像,想做成那種從清晰到模糊的動畫,估計還要調整一下代碼。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM