实现思路
模糊效果分为很多类型,如均值模糊,高斯模糊等等,模糊效果的实现都是对每一个像素,取它附近的一些像素,进行一些平均化的操作。因此每个像素和附近像素的差异就变小了,整个图也就变模糊了。
高斯模糊的做法是用二维正态分布,来计算每个每个像素应该占用的权值。而二维正态分布可以分成两个一维正态分布来计算。
代码实现
按思路实现即可,我们这里取横竖各五个像素来计算高斯模糊,当然也可以取更多的像素,只要根据正态分布函数计算出对应权值即可。
因为正态分布是一个偶函数所以我们这里也只用声明半边的权值。注意这里权值的总和需要为1
在片元着色器中依次遍历每个像素,获取rgb之后乘上权值就可以了,最后得到一个模糊化的像素返回。
fixed4 frag(v2f i) : SV_Target
{
float weight[3] = {0.4026, 0.2442, 0.0545};
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int index = 1; index < 3; index++)
{
sum += tex2D(_MainTex, i.uv[index]).rgb * weight[index];
sum += tex2D(_MainTex, i.uv[index+2]).rgb * weight[index];
}
return fixed4(sum, 1);
}
当然因为片元着色器中用到了5个像素的uv坐标,我们这里在顶点着色器中传入。
横竖分别要五个坐标,我们这里写了两个pass,一个是横向模糊,一个是竖向模糊。
v2f vertV(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
const half2 uv = v.uv;
o.uv[0] = uv;
o.uv[1] = uv + float2(0, _MainTex_TexelSize.y * 1);
o.uv[2] = uv + float2(0, _MainTex_TexelSize.y * 2);
o.uv[3] = uv + float2(0, _MainTex_TexelSize.y * -1);
o.uv[4] = uv + float2(0, _MainTex_TexelSize.y * -2);
return o;
}
v2f vertH(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
const half2 uv = v.uv;
o.uv[0] = uv;
o.uv[1] = uv + float2(0, _MainTex_TexelSize.x * 1);
o.uv[2] = uv + float2(0, _MainTex_TexelSize.x * 2);
o.uv[3] = uv + float2(0, _MainTex_TexelSize.x * -1);
o.uv[4] = uv + float2(0, _MainTex_TexelSize.x * -2);
return o;
}
在c#脚本中还可以声明一个迭代次数的变量,根据迭代次数来多次调用该shader进行多次高斯模糊,可以得到更加模糊的效果。
public class GaussianBlur : MonoBehaviour
{
public Shader shader;
public int iterationTime;
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
Material material = new Material(shader);
var buffer=RenderTexture.GetTemporary(src.width, src.height);
Graphics.Blit(src, buffer);
for (int i = 0; i < iterationTime; i++)
{
var buffer2=RenderTexture.GetTemporary(src.width, src.height);
Graphics.Blit(buffer, buffer2, material,0);
RenderTexture.ReleaseTemporary(buffer);
buffer = buffer2;
buffer2=RenderTexture.GetTemporary(src.width, src.height);
Graphics.Blit(buffer, buffer2, material,1);
RenderTexture.ReleaseTemporary(buffer);
buffer = buffer2;
}
Graphics.Blit(buffer, dest);
RenderTexture.ReleaseTemporary(buffer);
}
}
最后将c#脚本和shader挂在到同一个摄像机上,可以得到高斯模糊的效果如下。
完整代码
Shader "LX/GaussianBlur"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
ZTest Always
Cull Off
Zwrite Off
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
half2 uv[5]:TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _MainTex_TexelSize;
v2f vertV(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
const half2 uv = v.uv;
o.uv[0] = uv;
o.uv[1] = uv + float2(0, _MainTex_TexelSize.y * 1);
o.uv[2] = uv + float2(0, _MainTex_TexelSize.y * 2);
o.uv[3] = uv + float2(0, _MainTex_TexelSize.y * -1);
o.uv[4] = uv + float2(0, _MainTex_TexelSize.y * -2);
return o;
}
v2f vertH(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
const half2 uv = v.uv;
o.uv[0] = uv;
o.uv[1] = uv + float2(0, _MainTex_TexelSize.x * 1);
o.uv[2] = uv + float2(0, _MainTex_TexelSize.x * 2);
o.uv[3] = uv + float2(0, _MainTex_TexelSize.x * -1);
o.uv[4] = uv + float2(0, _MainTex_TexelSize.x * -2);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
float weight[3] = {0.4026, 0.2442, 0.0545};
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int index = 1; index < 3; index++)
{
sum += tex2D(_MainTex, i.uv[index]).rgb * weight[index];
sum += tex2D(_MainTex, i.uv[index+2]).rgb * weight[index];
}
return fixed4(sum, 1);
}
ENDCG
Pass
{
NAME "G_V"
CGPROGRAM
#pragma vertex vertV
#pragma fragment frag
ENDCG
}
Pass
{
NAME "G_H"
CGPROGRAM
#pragma vertex vertH
#pragma fragment frag
ENDCG
}
}
}