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

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

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

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

0.00300.01330.02190.01330.0030
0.01330.05960.09830.05960.0133
0.02190.09830.16210.09830.0219
0.01330.05960.09830.05960.0133
0.00300.01330.02190.01330.0030

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

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

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

拆分之后结果如下:

Unity Shader 屏幕后效果——高斯模糊-LMLPHP

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

具体实现:

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

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

 using UnityEngine;

 public class GaussianBlurCtrl : ScreenEffectBase
{
private const string _BlurSize = "_BlurSize";//只有模糊范围需要在GPU中计算 [Range(, )]
public int iterations = ;//迭代次数
[Range(0.2f, )]
public float blurSize = 0.6f;//模糊范围
[Range(, )]
public int downSample = ;//降采样系数 private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (Material != null)
{
//得到屏幕的渲染纹理后直接除以降采样系数以成倍减少计算量,但过大时模糊效果不佳
int rtw = source.width/downSample;
int rth = source.height/downSample; RenderTexture buffer0 = RenderTexture.GetTemporary(rtw, rth, );
buffer0.filterMode = FilterMode.Bilinear; Graphics.Blit(source, buffer0); //利用迭代次数对模糊范围加以控制,用到了类似于双缓冲的方式对纹理进行处理
for (int i = ; i < iterations; i++)
{
//设置采样范围,根据迭代次数范围增加,之后会与纹理坐标进行乘积操作,固基础值为1
Material.SetFloat(_BlurSize, blurSize*i+); RenderTexture buffer1 = RenderTexture.GetTemporary(rtw, rth, );
Graphics.Blit(buffer0, buffer1, Material, );
//每次处理完立即释放相应缓存,因为Unity内部已经对此做了相应的优化
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = RenderTexture.GetTemporary(rtw, rth, );
Graphics.Blit(buffer1, buffer0,Material, );
RenderTexture.ReleaseTemporary(buffer1);
}
Graphics.Blit(buffer0, destination);
RenderTexture.ReleaseTemporary(buffer0);
}
else
Graphics.Blit(source, destination);
}
}

基类脚本见:

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

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

 Shader "MyUnlit/GaussianBlur"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" } //CGINCLUDE中的代码可被其他Pass重复调用,用于简化不必要的重复代码
CGINCLUDE #pragma multi_compile_fog
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}; struct v2f
{
half2 uv[] : TEXCOORD0;
UNITY_FOG_COORDS()
float4 pos : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_TexelSize;
float _BlurSize; //用于计算纵向模糊的纹理坐标元素
v2f vert_v(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.uv; //以扩散的方式对数组进行排序,只偏移y轴,其中1和2,3和4分别位于原始点0的上下,且距离1个单位和2个像素单位
//得到的最终偏移与模糊范围的控制参数进行乘积
o.uv[] = uv;
o.uv[] = uv + float2(0.0, _MainTex_TexelSize.y*1.0)*_BlurSize;
o.uv[] = uv - float2(0.0, _MainTex_TexelSize.y*1.0)*_BlurSize;
o.uv[] = uv + float2(0.0, _MainTex_TexelSize.y*2.0)*_BlurSize;
o.uv[] = uv - float2(0.0, _MainTex_TexelSize.y*2.0)*_BlurSize; UNITY_TRANSFER_FOG(o, o.vertex);
return o;
} //用于计算横向模糊的纹理坐标元素
v2f vert_h(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.uv; //与上面同理,只不过是x轴向的模糊偏移
o.uv[] = uv;
o.uv[] = uv + float2( _MainTex_TexelSize.x*1.0,0.0)*_BlurSize;
o.uv[] = uv - float2( _MainTex_TexelSize.x*1.0,0.0)*_BlurSize;
o.uv[] = uv + float2( _MainTex_TexelSize.x*2.0,0.0)*_BlurSize;
o.uv[] = uv - float2( _MainTex_TexelSize.x*2.0,0.0)*_BlurSize; UNITY_TRANSFER_FOG(o, o.vertex);
return o;
} //在片元着色器中进行最终的模糊计算,此过程在每个Pass中都会进行一次计算,但计算方式是统一的
fixed4 frag(v2f i) : SV_Target
{
float weights[] = {0.4026,0.2442,0.0545}; fixed4 col = tex2D(_MainTex, i.uv[]); fixed3 sum = col.rgb*weights[]; //对采样结果进行对应纹理偏移坐标的权重计算,以得到模糊的效果
for (int it = ; it < ; it++)
{
sum += tex2D(_MainTex, i.uv[ * it - ]).rgb*weights[it];//对应1和3,也就是原始像素的上方两像素
sum += tex2D(_MainTex, i.uv[ * it]).rgb*weights[it];//对应2和4,下方两像素
}
fixed4 color = fixed4(sum, 1.0);
UNITY_APPLY_FOG(i.fogCoord, color);
return color;
} ENDCG ZTest Always
Cull Off
ZWrite Off //纵向模糊Pass,直接用指令调用上面的函数
Pass
{
NAME "GAUSSIANBLUR_V"
CGPROGRAM
#pragma vertex vert_v
#pragma fragment frag ENDCG
} //横向模糊Pass
Pass
{
NAME "GAUSSIANBLUR_H"
CGPROGRAM
#pragma vertex vert_h
#pragma fragment frag ENDCG
}
}
Fallback Off
}

效果如下:

Unity Shader 屏幕后效果——高斯模糊-LMLPHP

05-18 18:12