原理

利用另一个算子,均值滤波器,这个算子的作用可以平均邻域内的像素,从而达到模糊的效果

高斯滤波:很好的模拟了邻域每个像素对当前像素的影响程度:距离越近,影响越大。高斯核的维数越高,模糊程度越大。

实现

把5x5的高斯核变成横向和纵向的两个一位高斯核,用两个pass来得到最后的图像,通过图像缩放提高性能,通过应用次数来控制模糊程度

脚本类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using UnityEngine;
using System.Collections;

public class GaussianBlur : PostEffectsBase {

public Shader gaussianBlurShader;
private Material gaussianBlurMaterial = null;

public Material material {
get {
gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);
return gaussianBlurMaterial;
}
}

// Blur iterations - larger number means more blur.
//迭代次数,模糊范围,缩放系数
[Range(0, 4)]
public int iterations = 3;

// Blur spread for each iteration - larger value means more blur
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f;

[Range(1, 8)]
public int downSample = 2;

/// 1st edition: just apply blur
// void OnRenderImage(RenderTexture src, RenderTexture dest) {
// if (material != null) {
// int rtW = src.width;
// int rtH = src.height;
//通过该函数分配了一块与屏幕图像大小相同的缓冲区,用来存放第一个pass的结果
// RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
//
// // Render the vertical pass
// Graphics.Blit(src, buffer, material, 0);
// // Render the horizontal pass
// Graphics.Blit(buffer, dest, material, 1);
//
// RenderTexture.ReleaseTemporary(buffer);
// } else {
// Graphics.Blit(src, dest);
// }
// }

/// 2nd edition: scale the render texture
// void OnRenderImage (RenderTexture src, RenderTexture dest) {
// if (material != null)
//让缓冲区大小缩小,使用双线性插值,当把图片导出缓冲区的时候,多个像素归并为一个,可以减少像素数,可以提供性能的同时提供更好的模糊效果
// int rtW = src.width/downSample;
// int rtH = src.height/downSample;
// RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
// buffer.filterMode = FilterMode.Bilinear;
//
// // Render the vertical pass
// Graphics.Blit(src, buffer, material, 0);
// // Render the horizontal pass
// Graphics.Blit(buffer, dest, material, 1);
//
// RenderTexture.ReleaseTemporary(buffer);
// } else {
// Graphics.Blit(src, dest);
// }
// }

/// 3rd edition: use iterations for larger blur
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
int rtW = src.width/downSample;
int rtH = src.height/downSample;

RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
buffer0.filterMode = FilterMode.Bilinear;

Graphics.Blit(src, buffer0);

for (int i = 0; i < iterations; i++) {
//每一次迭代的时候,都扩大模糊范围
material.SetFloat("_BlurSize", 1.0f + i * blurSpread);

RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

// Render the vertical pass
Graphics.Blit(buffer0, buffer1, material, 0);

RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

// Render the horizontal pass
Graphics.Blit(buffer0, buffer1, material, 1);

RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
}

Graphics.Blit(buffer0, dest);
RenderTexture.ReleaseTemporary(buffer0);
} else {
Graphics.Blit(src, dest);
}
}
}

shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 12/Gaussian Blur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Float) = 1.0
}
SubShader {
CGINCLUDE

#include "UnityCG.cginc"

sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;

struct v2f {
float4 pos : SV_POSITION;
half2 uv[5]: TEXCOORD0;
};

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) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);

half2 uv = v.texcoord;

o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;

return o;
}

fixed4 fragBlur(v2f i) : SV_Target {
//由于5x5,其实是对称的,所以只需要知道一边的参数值就行
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"

CGPROGRAM

#pragma vertex vertBlurVertical
#pragma fragment fragBlur

ENDCG
}

Pass {
NAME "GAUSSIAN_BLUR_HORIZONTAL"

CGPROGRAM

#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur

ENDCG
}
}
FallBack "Diffuse"
}