Unity Shader 学习笔记(21) 模糊、高斯模糊
参考书籍:《Unity Shader 入门精要》
GaussianBlur类源码:GaussianBlur.cs
模糊
- 均值模糊:卷积核各个元素都相等,且相加为1。即卷积后得到像素值是邻域内各像素平均值。
- 中值模糊:即邻域内所有像素排序后中间那个,替换掉原颜色。
- 高斯模糊:使用卷积核称作高斯核,高斯核是正方形的滤波核。
高斯模糊
高斯方程如下。σ为标准方差(一般为1)。即离得越近,影响越大,越模糊。
对应类似如下图的正态分布:
因为高斯核是正态分布的,所以可以简化高斯核:
迭代次数逐渐提高,每次迭代模糊采样跨度扩大,性能消耗增加:
迭代次数不变下(一次),提高模糊采样跨度:
减小屏幕获取纹理大小,性能消耗减小,可能会像素化:
GaussianBlur类(省略其他两个版本,可见最上方源码):
public class GaussianBlur : PostEffectsBase
{
[Range(0, 4)]
public int iterations = 3; // 模糊迭代次数
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f; // 模糊范围,过大会造成虚影
[Range(1, 8)]
public int downSample = 2; // 减少采样倍数的平方。越大,处理像素越少,过大可能会像素化
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (TargetMaterial != null)
{
int rtW = src.width / downSample;
int rtH = src.height / downSample;
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
buffer0.filterMode = FilterMode.Bilinear;
Graphics.Blit(src, buffer0); // 用到所有Pass块
// buffer0 存将要被处理的缓存,buffer1存搞好的
for (int i = 0; i < iterations; i++)
{
material.SetFloat("_BlurSize", 1.0f + (i + 1) * blurSpread);
Graphics.Blit(buffer0, buffer1, material, 0);
Graphics.Blit(buffer1, buffer0, material, 1);
}
Graphics.Blit(buffer0, dest);
RenderTexture.ReleaseTemporary(buffer0);
RenderTexture.ReleaseTemporary(buffer1);
}
else
Graphics.Blit(src, dest);
}
}
Shader:
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Float) = 1.0 // 模糊采样距离,过大会产生虚影
}
SubShader {
// 类似C++头文件功能。使用时不用包含在Pass块,Pass中直接指定顶点和片元着色器,可避免编写两个一样的frag函数
CGINCLUDE
...
struct v2f {
float4 pos : SV_POSITION;
half2 uv[5]: TEXCOORD0;
};
// appdata_img定义在UnityCG.cginc
v2f vertBlurVertical(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
v2f vertBlurHorizontal(appdata_img v) {
... // 类似处理垂直模糊的顶点着色器
}
// 两个Pass公用片元着色器
fixed4 fragBlur(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 it = 1; it < 3; it++) {
sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it]; // 和下面对称的点
sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
}
return fixed4(sum, 1.0);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
NAME "GAUSSIAN_BLUR_VERTICAL" // 定义名字。可以从其他Shader直接来使用该Pass
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass {
NAME "GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}