001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
Shader
"Learning Unity Shader/Lecture 15/RapidBlurEffect"
{
//-----------------------------------【属性 || Properties】------------------------------------------
Properties
{
//主纹理
_MainTex(
"Base (RGB)"
, 2D) =
"white"
{}
}
//----------------------------------【子着色器 || SubShader】---------------------------------------
SubShader
{
ZWrite Off
Blend Off
//---------------------------------------【通道0 || Pass 0】------------------------------------
//通道0:降采样通道 ||Pass 0: Down Sample Pass
Pass
{
ZTest Off
Cull Off
CGPROGRAM
//指定此通道的顶点着色器为vert_DownSmpl
#pragma vertex vert_DownSmpl
//指定此通道的像素着色器为frag_DownSmpl
#pragma fragment frag_DownSmpl
ENDCG
}
//---------------------------------------【通道1 || Pass 1】------------------------------------
//通道1:垂直方向模糊处理通道 ||Pass 1: Vertical Pass
Pass
{
ZTest Always
Cull Off
CGPROGRAM
//指定此通道的顶点着色器为vert_BlurVertical
#pragma vertex vert_BlurVertical
//指定此通道的像素着色器为frag_Blur
#pragma fragment frag_Blur
ENDCG
}
//---------------------------------------【通道2 || Pass 2】------------------------------------
//通道2:水平方向模糊处理通道 ||Pass 2: Horizontal Pass
Pass
{
ZTest Always
Cull Off
CGPROGRAM
//指定此通道的顶点着色器为vert_BlurHorizontal
#pragma vertex vert_BlurHorizontal
//指定此通道的像素着色器为frag_Blur
#pragma fragment frag_Blur
ENDCG
}
}
//-------------------------CG着色语言声明部分 || Begin CG Include Part----------------------
CGINCLUDE
//【1】头文件包含 || include
#include "UnityCG.cginc"
//【2】变量声明 || Variable Declaration
sampler2D _MainTex;
//UnityCG.cginc中内置的变量,纹理中的单像素尺寸|| it is the size of a texel of the texture
uniform half4 _MainTex_TexelSize;
//C#脚本控制的变量 || Parameter
uniform half _DownSampleValue;
//【3】顶点输入结构体 || Vertex Input Struct
struct
VertexInput
{
//顶点位置坐标
float4 vertex : POSITION;
//一级纹理坐标
half2 texcoord : TEXCOORD0;
};
//【4】降采样输出结构体 || Vertex Input Struct
struct
VertexOutput_DownSmpl
{
//像素位置坐标
float4 pos : SV_POSITION;
//一级纹理坐标(右上)
half2 uv20 : TEXCOORD0;
//二级纹理坐标(左下)
half2 uv21 : TEXCOORD1;
//三级纹理坐标(右下)
half2 uv22 : TEXCOORD2;
//四级纹理坐标(左上)
half2 uv23 : TEXCOORD3;
};
//【5】准备高斯模糊权重矩阵参数7x4的矩阵 || Gauss Weight
static
const
half4 GaussWeight[7] =
{
half4(0.0205,0.0205,0.0205,0),
half4(0.0855,0.0855,0.0855,0),
half4(0.232,0.232,0.232,0),
half4(0.324,0.324,0.324,1),
half4(0.232,0.232,0.232,0),
half4(0.0855,0.0855,0.0855,0),
half4(0.0205,0.0205,0.0205,0)
};
//【6】顶点着色函数 || Vertex Shader Function
VertexOutput_DownSmpl vert_DownSmpl(VertexInput v)
{
//【6.1】实例化一个降采样输出结构
VertexOutput_DownSmpl o;
//【6.2】填充输出结构
//将三维空间中的坐标投影到二维窗口
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//对图像的降采样:取像素上下左右周围的点,分别存于四级纹理坐标中
o.uv20 = v.texcoord + _MainTex_TexelSize.xy* half2(0.5h, 0.5h);;
o.uv21 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h, -0.5h);
o.uv22 = v.texcoord + _MainTex_TexelSize.xy * half2(0.5h, -0.5h);
o.uv23 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h, 0.5h);
//【6.3】返回最终的输出结果
return
o;
}
//【7】片段着色函数 || Fragment Shader Function
fixed4 frag_DownSmpl(VertexOutput_DownSmpl i) : SV_Target
{
//【7.1】定义一个临时的颜色值
fixed4 color = (0,0,0,0);
//【7.2】四个相邻像素点处的纹理值相加
color += tex2D(_MainTex, i.uv20);
color += tex2D(_MainTex, i.uv21);
color += tex2D(_MainTex, i.uv22);
color += tex2D(_MainTex, i.uv23);
//【7.3】返回最终的平均值
return
color / 4;
}
//【8】顶点输入结构体 || Vertex Input Struct
struct
VertexOutput_Blur
{
//像素坐标
float4 pos : SV_POSITION;
//一级纹理(纹理坐标)
half4 uv : TEXCOORD0;
//二级纹理(偏移量)
half2 offset : TEXCOORD1;
};
//【9】顶点着色函数 || Vertex Shader Function
VertexOutput_Blur vert_BlurHorizontal(VertexInput v)
{
//【9.1】实例化一个输出结构
VertexOutput_Blur o;
//【9.2】填充输出结构
//将三维空间中的坐标投影到二维窗口
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//纹理坐标
o.uv = half4(v.texcoord.xy, 1, 1);
//计算X方向的偏移量
o.offset = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _DownSampleValue;
//【9.3】返回最终的输出结果
return
o;
}
//【10】顶点着色函数 || Vertex Shader Function
VertexOutput_Blur vert_BlurVertical(VertexInput v)
{
//【10.1】实例化一个输出结构
VertexOutput_Blur o;
//【10.2】填充输出结构
//将三维空间中的坐标投影到二维窗口
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//纹理坐标
o.uv = half4(v.texcoord.xy, 1, 1);
//计算Y方向的偏移量
o.offset = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _DownSampleValue;
//【10.3】返回最终的输出结果
return
o;
}
//【11】片段着色函数 || Fragment Shader Function
half4 frag_Blur(VertexOutput_Blur i) : SV_Target
{
//【11.1】获取原始的uv坐标
half2 uv = i.uv.xy;
//【11.2】获取偏移量
half2 OffsetWidth = i.offset;
//从中心点偏移3个间隔,从最左或最上开始加权累加
half2 uv_withOffset = uv - OffsetWidth * 3.0;
//【11.3】循环获取加权后的颜色值
half4 color = 0;
for
(
int
j = 0; j< 7; j++)
{
//偏移后的像素纹理值
half4 texCol = tex2D(_MainTex, uv_withOffset);
//待输出颜色值+=偏移后的像素纹理值 x 高斯权重
color += texCol * GaussWeight[j];
//移到下一个像素处,准备下一次循环加权
uv_withOffset += OffsetWidth;
}
//【11.4】返回最终的颜色值
return
color;
}
//-------------------结束CG着色语言声明部分 || End CG Programming Part------------------
ENDCG
FallBack Off
}
|
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
using
UnityEngine;
using
System.Collections;
//设置在编辑模式下也执行该脚本
[ExecuteInEditMode]
//添加选项到菜单中
[AddComponentMenu(
"Learning Unity Shader/Lecture 15/RapidBlurEffect"
)]
public
class
RapidBlurEffect : MonoBehaviour
{
//-------------------变量声明部分-------------------
#region Variables
//指定Shader名称
private
string
ShaderName =
"Learning Unity Shader/Lecture 15/RapidBlurEffect"
;
//着色器和材质实例
public
Shader CurShader;
private
Material CurMaterial;
//几个用于调节参数的中间变量
public
static
int
ChangeValue;
public
static
float
ChangeValue2;
public
static
int
ChangeValue3;
//降采样次数
[Range(0, 6), Tooltip(
"[降采样次数]向下采样的次数。此值越大,则采样间隔越大,需要处理的像素点越少,运行速度越快。"
)]
public
int
DownSampleNum = 2;
//模糊扩散度
[Range(0.0f, 20.0f), Tooltip(
"[模糊扩散度]进行高斯模糊时,相邻像素点的间隔。此值越大相邻像素间隔越远,图像越模糊。但过大的值会导致失真。"
)]
public
float
BlurSpreadSize = 3.0f;
//迭代次数
[Range(0, 8), Tooltip(
"[迭代次数]此值越大,则模糊操作的迭代次数越多,模糊效果越好,但消耗越大。"
)]
public
int
BlurIterations = 3;
#endregion
//-------------------------材质的get&set----------------------------
#region MaterialGetAndSet
Material material
{
get
{
if
(CurMaterial ==
null
)
{
CurMaterial =
new
Material(CurShader);
CurMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return
CurMaterial;
}
}
#endregion
#region Functions
//-----------------------------------------【Start()函数】---------------------------------------------
// 说明:此函数仅在Update函数第一次被调用前被调用
//--------------------------------------------------------------------------------------------------------
void
Start()
{
//依次赋值
ChangeValue = DownSampleNum;
ChangeValue2 = BlurSpreadSize;
ChangeValue3 = BlurIterations;
//找到当前的Shader文件
CurShader = Shader.Find(ShaderName);
//判断当前设备是否支持屏幕特效
if
(!SystemInfo.supportsImageEffects)
{
enabled =
false
;
return
;
}
}
//-------------------------------------【OnRenderImage()函数】------------------------------------
// 说明:此函数在当完成所有渲染图片后被调用,用来渲染图片后期效果
//--------------------------------------------------------------------------------------------------------
void
OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
{
//着色器实例不为空,就进行参数设置
if
(CurShader !=
null
)
{
//【0】参数准备
//根据向下采样的次数确定宽度系数。用于控制降采样后相邻像素的间隔
float
widthMod = 1.0f / (1.0f * (1 << DownSampleNum));
//Shader的降采样参数赋值
material.SetFloat(
"_DownSampleValue"
, BlurSpreadSize * widthMod);
//设置渲染模式:双线性
sourceTexture.filterMode = FilterMode.Bilinear;
//通过右移,准备长、宽参数值
int
renderWidth = sourceTexture.width >> DownSampleNum;
int
renderHeight = sourceTexture.height >> DownSampleNum;
// 【1】处理Shader的通道0,用于降采样 ||Pass 0,for down sample
//准备一个缓存renderBuffer,用于准备存放最终数据
RenderTexture renderBuffer = RenderTexture.GetTemporary(renderWidth, renderHeight, 0, sourceTexture.format);
//设置渲染模式:双线性
renderBuffer.filterMode = FilterMode.Bilinear;
//拷贝sourceTexture中的渲染数据到renderBuffer,并仅绘制指定的pass0的纹理数据
Graphics.Blit(sourceTexture, renderBuffer, material, 0);
//【2】根据BlurIterations(迭代次数),来进行指定次数的迭代操作
for
(
int
i = 0; i < BlurIterations; i++)
{
//【2.1】Shader参数赋值
//迭代偏移量参数
float
iterationOffs = (i * 1.0f);
//Shader的降采样参数赋值
material.SetFloat(
"_DownSampleValue"
, BlurSpreadSize * widthMod + iterationOffs);
// 【2.2】处理Shader的通道1,垂直方向模糊处理 || Pass1,for vertical blur
// 定义一个临时渲染的缓存tempBuffer
RenderTexture tempBuffer = RenderTexture.GetTemporary(renderWidth, renderHeight, 0, sourceTexture.format);
// 拷贝renderBuffer中的渲染数据到tempBuffer,并仅绘制指定的pass1的纹理数据
Graphics.Blit(renderBuffer, tempBuffer, material, 1);
// 清空renderBuffer
RenderTexture.ReleaseTemporary(renderBuffer);
// 将tempBuffer赋给renderBuffer,此时renderBuffer里面pass0和pass1的数据已经准备好
renderBuffer = tempBuffer;
// 【2.3】处理Shader的通道2,竖直方向模糊处理 || Pass2,for horizontal blur
// 获取临时渲染纹理
tempBuffer = RenderTexture.GetTemporary(renderWidth, renderHeight, 0, sourceTexture.format);
// 拷贝renderBuffer中的渲染数据到tempBuffer,并仅绘制指定的pass2的纹理数据
Graphics.Blit(renderBuffer, tempBuffer, CurMaterial, 2);
//【2.4】得到pass0、pass1和pass2的数据都已经准备好的renderBuffer
// 再次清空renderBuffer
RenderTexture.ReleaseTemporary(renderBuffer);
// 再次将tempBuffer赋给renderBuffer,此时renderBuffer里面pass0、pass1和pass2的数据都已经准备好
renderBuffer = tempBuffer;
}
//拷贝最终的renderBuffer到目标纹理,并绘制所有通道的纹理到屏幕
Graphics.Blit(renderBuffer, destTexture);
//清空renderBuffer
RenderTexture.ReleaseTemporary(renderBuffer);
}
//着色器实例为空,直接拷贝屏幕上的效果。此情况下是没有实现屏幕特效的
else
{
//直接拷贝源纹理到目标渲染纹理
Graphics.Blit(sourceTexture, destTexture);
}
}
//-----------------------------------------【OnValidate()函数】--------------------------------------
// 说明:此函数在编辑器中该脚本的某个值发生了改变后被调用
//--------------------------------------------------------------------------------------------------------
void
|