zoukankan      html  css  js  c++  java
  • Unity Shader 屏幕后效果——高斯模糊

    高斯模糊是图像模糊处理中非常经典和常见的一种算法,也是Bloom屏幕效果的基础。

    实现高斯模糊同样用到了卷积的概念,关于卷积的概念和原理详见我的另一篇博客:

     https://www.cnblogs.com/koshio0219/p/11137155.html

    通过高斯方程计算出的卷积核称为高斯核,一个5*5的高斯核对它进行权重归一化如下:

    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

    通过表也可以很清楚的看到,离原点越近的点模糊程度影响越大,反之越小。

    为了优化计算,可以将这个5*5矩阵简化为两个矩阵分别计算,得到的效果是相同的。

    它们分别是一个1*5的横向矩阵和一个5*1的纵向矩阵,这样我们只需要对横纵向矩阵分别进行一次采样既可,这样可以很大程度的减少计算量。

    拆分之后结果如下:

     

     我们发现,最终的计算只需要记录3个权重值既可,它们是weight[3]={0.4026,0.2442,0.0545};

    具体实现:

    1.实现调整高斯模糊参数的脚本。

    为了进一步优化计算,这里加入了降采样系数,模糊范围缩放;为此,需要在外部增加模糊采样的迭代次数,具体如下:

     1 using UnityEngine;
     2 
     3 public class GaussianBlurCtrl : ScreenEffectBase
     4 {
     5     private const string _BlurSize = "_BlurSize";//只有模糊范围需要在GPU中计算
     6 
     7     [Range(0, 4)]
     8     public int iterations = 3;//迭代次数
     9     [Range(0.2f, 3)]
    10     public float blurSize = 0.6f;//模糊范围
    11     [Range(1, 8)]
    12     public int downSample = 2;//降采样系数
    13 
    14     private void OnRenderImage(RenderTexture source, RenderTexture destination)
    15     {
    16         if (Material != null)
    17         {
    18             //得到屏幕的渲染纹理后直接除以降采样系数以成倍减少计算量,但过大时模糊效果不佳
    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             //利用迭代次数对模糊范围加以控制,用到了类似于双缓冲的方式对纹理进行处理
    28             for (int i = 0; i < iterations; i++)
    29             {
    30                 //设置采样范围,根据迭代次数范围增加,之后会与纹理坐标进行乘积操作,固基础值为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                 //每次处理完立即释放相应缓存,因为Unity内部已经对此做了相应的优化
    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 }

    基类脚本见:

    https://www.cnblogs.com/koshio0219/p/11131619.html

    2.在Shader中分别进行横向和纵向的模糊计算,分为两个Pass进行,具体如下:

      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重复调用,用于简化不必要的重复代码
     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         //用于计算纵向模糊的纹理坐标元素
     35         v2f vert_v(appdata v)
     36         {
     37             v2f o;
     38             o.pos = UnityObjectToClipPos(v.vertex);
     39             half2 uv = v.uv;
     40 
     41             //以扩散的方式对数组进行排序,只偏移y轴,其中1和2,3和4分别位于原始点0的上下,且距离1个单位和2个像素单位
     42             //得到的最终偏移与模糊范围的控制参数进行乘积
     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         //用于计算横向模糊的纹理坐标元素
     54         v2f vert_h(appdata v)
     55         {
     56             v2f o;
     57             o.pos = UnityObjectToClipPos(v.vertex);
     58             half2 uv = v.uv;
     59 
     60             //与上面同理,只不过是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         //在片元着色器中进行最终的模糊计算,此过程在每个Pass中都会进行一次计算,但计算方式是统一的
     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             //对采样结果进行对应纹理偏移坐标的权重计算,以得到模糊的效果
     81             for (int it = 1; it < 3; it++) 
     82             {
     83                 sum += tex2D(_MainTex, i.uv[2 * it - 1]).rgb*weights[it];//对应1和3,也就是原始像素的上方两像素
     84                 sum += tex2D(_MainTex, i.uv[2 * it]).rgb*weights[it];//对应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,直接用指令调用上面的函数
     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 }

    效果如下:

  • 相关阅读:
    iSeries存储过程笔记
    最近做得的网站
    两个iSeries存储过程的示例
    索引的怪异问题
    Saas的概念感触
    asp.net 2.0的TextBox遭遇ReadOnly=True时ViewState不回传的问题
    网上看到的一句诗
    DB2/400到Oracle的迁移
    如何往自己的网站增加Asp.net Ajax
    default关键字作用
  • 原文地址:https://www.cnblogs.com/koshio0219/p/11152534.html
Copyright © 2011-2022 走看看