Galgame 演出所使用的 Shader
桶形畸变
Barrel
“桶形畸变”(Barrel Distortion)通常用于模拟相机镜头的畸变效果。着色器的目的是基于给定的纹理(_MainTex
)对图像进行扭曲处理,并提供一定的可调参数来控制该效果的强度、色差等。
该着色器实现了一个带有桶形畸变和可调色差的效果,能够根据不同的参数(如曲率、缩放、色差强度等)对图像进行不同程度的扭曲处理。常见的应用场景是模拟相机镜头畸变、镜头效果,或创造类似于镜头变形的视觉效果。
_MainTex
: 纹理,用作图像源。默认为白色。
_T
: 用于控制时间或动画效果的浮动值(在0到1之间)。
_Sigma
: 影响畸变程度的参数,通常称为曲率。
_Aspect
: 画面的宽高比,用于调整畸变的形状。
_Scale
: 影响纹理的缩放比例。
_Chroma
: 色差强度,通常用于模拟颜色的分离效果(类似色差效应)。
_Offset
: 色彩偏移,影响颜色的亮度。
_BackColor
: 背景色,如果纹理坐标超出[0, 1]范围时使用的颜色。
1Shader "Nova/$VARIANT_NAME$/Barrel"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Sigma ("Curvature", Float) = 0.2
8 _Aspect ("Aspect Ratio", Float) = 1.77777778
9 _Scale ("Scale", Float) = 1.0
10 _Chroma ("Chromatic Aberration Strength", Float) = 0.0
11 _Offset ("Offset", Float) = 0.0
12 _BackColor ("Background Color", Color) = (0, 0, 0, 1)
13 }
14 SubShader
15 {
16 $VARIANT_TAGS$
17 Pass
18 {
19 CGPROGRAM
20 #pragma vertex vert
21 #pragma fragment frag
22
23 #include "UnityCG.cginc"
24
25 #define PI_HALF 1.5707963
26
27 struct appdata
28 {
29 float4 vertex : POSITION;
30 float2 uv : TEXCOORD0;
31 float4 color : COLOR;
32 };
33
34 struct v2f
35 {
36 float4 vertex : SV_POSITION;
37 float2 uv : TEXCOORD0;
38 float4 color : COLOR;
39 };
40
41 v2f vert(appdata v)
42 {
43 v2f o;
44 o.vertex = UnityObjectToClipPos(v.vertex);
45 o.uv = v.uv;
46 o.color = v.color;
47 return o;
48 }
49
50 // 这个函数是桶形畸变的核心。它根据给定的曲率(sigma)和其他参数,对uv坐标进行扭曲处理。
51 // 通过调整uv坐标来模拟镜头的畸变效果。
52 // sigma 控制畸变的程度,aspect 控制宽高比,scale 控制纹理坐标的缩放。
53 float2 convertUV(float2 uv, float sigma, float aspect, float scale)
54 {
55 uv -= 0.5;
56 uv /= scale;
57 float r = sqrt(uv.x * uv.x * aspect * aspect + uv.y * uv.y);
58
59 float ratio;
60 float sinTheta = r * sigma * 4.0;
61 if (sinTheta < 0.1)
62 {
63 ratio = 1.0 + sinTheta * sinTheta / 6.0; // Taylor expansion
64 }
65 else if (sinTheta > 1.0)
66 {
67 ratio = PI_HALF * sinTheta;
68 }
69 else
70 {
71 ratio = asin(sinTheta) / sinTheta;
72 }
73
74 uv *= ratio;
75 uv += 0.5;
76 return uv;
77 }
78
79 sampler2D _MainTex;
80 float _Aspect, _Scale;
81 float4 _BackColor;
82
83 // 该函数通过调用convertUV函数来计算畸变后的纹理坐标,接着根据计算出的坐标从纹理中获取颜色
84 // 如果坐标超出了纹理的边界,则返回背景色(_BackColor)。
85 float4 getColor(float2 uv, float sigma)
86 {
87 uv = convertUV(uv, sigma, _Aspect, _Scale);
88 if (uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0)
89 {
90 return tex2D(_MainTex, uv);
91 }
92 else
93 {
94 return _BackColor;
95 }
96 }
97
98 float _T, _Sigma, _Chroma, _Offset;
99
100 fixed4 frag(v2f i) : SV_Target
101 {
102 float sigma = _Sigma * _T;
103
104 float4 col;
105 if (_Chroma > 0.0)
106 {
107 float r = getColor(i.uv, sigma * (1.0 - _Chroma)).r;
108 float2 ga = getColor(i.uv, sigma).ga;
109 float b = getColor(i.uv, sigma * (1.0 + _Chroma)).b;
110 col = float4(r, ga.x, b, ga.y);
111 }
112 else
113 {
114 col = getColor(i.uv, sigma);
115 }
116
117 col *= i.color;
118 col.rgb += _Offset * _T;
119 col.rgb = saturate(col.rgb);
120
121 $VARIANT_RGB$
122
123 return col;
124 }
125 ENDCG
126 }
127 }
128}
Barrel Hyper
这段代码是一个名为“Barrel Hyper”的Unity着色器。它是基于桶形畸变(Barrel Distortion)效果,进一步扩展或增强了畸变效果的强度,可能会产生更为强烈的镜头变形。它的实现和前一个“Barrel”着色器类似,但采用了不同的畸变公式。
1Shader "Nova/$VARIANT_NAME$/Barrel Hyper"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Sigma ("Curvature", Float) = 0.2
8 _Aspect ("Aspect Ratio", Float) = 1.77777778
9 _Scale ("Scale", Float) = 1.0
10 _Chroma ("Chromatic Aberration Strength", Float) = 0.0
11 _Offset ("Offset", Float) = 0.0
12 _BackColor ("Background Color", Color) = (0, 0, 0, 1)
13 }
14 SubShader
15 {
16 $VARIANT_TAGS$
17 Pass
18 {
19 CGPROGRAM
20 #pragma vertex vert
21 #pragma fragment frag
22
23 #include "UnityCG.cginc"
24
25 struct appdata
26 {
27 float4 vertex : POSITION;
28 float2 uv : TEXCOORD0;
29 float4 color : COLOR;
30 };
31
32 struct v2f
33 {
34 float4 vertex : SV_POSITION;
35 float2 uv : TEXCOORD0;
36 float4 color : COLOR;
37 };
38
39 v2f vert(appdata v)
40 {
41 v2f o;
42 o.vertex = UnityObjectToClipPos(v.vertex);
43 o.uv = v.uv;
44 o.color = v.color;
45 return o;
46 }
47
48 // 这是桶形畸变的关键部分。它与前一个着色器的convertUV类似,但公式略有不同。在这里:
49 // 先将uv从中心(0.5, 0.5)平移,使其原点对准图像中心。
50 // 然后根据sigma(曲率)和r(从中心到当前uv坐标的距离)计算畸变的比例因子ratio
51 // 该因子使得距离越远的区域畸变效果越强。
52 // 最后,将UV坐标恢复到[0,1]的范围。
53 float2 convertUV(float2 uv, float sigma, float aspect, float scale)
54 {
55 uv -= 0.5;
56 uv /= scale;
57 float r = sqrt(uv.x * uv.x * aspect * aspect + uv.y * uv.y);
58 float ratio = 1.0 - r * sigma;
59 uv *= ratio;
60 uv += 0.5;
61 return uv;
62 }
63
64 sampler2D _MainTex;
65 float _Aspect, _Scale;
66 float4 _BackColor;
67
68 // 该函数使用convertUV来调整uv坐标,然后从_MainTex纹理中采样颜色。
69 // 如果uv坐标超出了[0,1]的范围,返回背景色(_BackColor)。
70 float4 getColor(float2 uv, float sigma)
71 {
72 uv = convertUV(uv, sigma, _Aspect, _Scale);
73 if (uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0)
74 {
75 return tex2D(_MainTex, uv);
76 }
77 else
78 {
79 return _BackColor;
80 }
81 }
82
83 float _T, _Sigma, _Chroma, _Offset;
84
85 // 片段着色器的核心逻辑是从纹理中获取颜色,并根据时间、曲率、色差等因素进行处理。
86 // sigma:根据时间_T和曲率_Sigma计算畸变的强度。
87 // 如果_Chroma(色差)大于零,调整红色和蓝色通道的畸变程度,以产生色差效果。
88 // 最后颜色会与顶点颜色(i.color)相乘,可能还会加入一个偏移量_Offset,使得图像亮度随时间变化
89 // saturate(col.rgb)确保颜色值不超过[0,1]的范围,避免颜色溢出。
90 fixed4 frag(v2f i) : SV_Target
91 {
92 float sigma = _Sigma * _T;
93
94 float4 col;
95 if (_Chroma > 0.0)
96 {
97 float r = getColor(i.uv, sigma * (1.0 - _Chroma)).r;
98 float2 ga = getColor(i.uv, sigma).ga;
99 float b = getColor(i.uv, sigma * (1.0 + _Chroma)).b;
100 col = float4(r, ga.x, b, ga.y);
101 }
102 else
103 {
104 col = getColor(i.uv, sigma);
105 }
106
107 col *= i.color;
108 col.rgb += _Offset * _T;
109 col.rgb = saturate(col.rgb);
110
111 $VARIANT_RGB$
112
113 return col;
114 }
115 ENDCG
116 }
117 }
118}
Blink
Blink 是通过时间和频率参数的控制,使纹理的颜色在屏幕上闪烁或波动,产生类似“眨眼”或闪烁的视觉效果。着色器中使用了噪声和时间控制来动态改变颜色,并通过一些其他参数来控制效果的强度和频率。
创建一个动态变化的“闪烁”效果,常用于视觉上的闪烁或类似眨眼的效果。通过控制时间(_T
)、振幅(_Amp
)、频率(_Freq
)等参数,可以调节颜色的变化幅度和频率。该着色器使用噪声来驱动颜色的变化,从而让图像颜色看起来在某种周期性方式上变化(例如,颜色逐渐变暗、变亮,产生闪烁的效果)。
_MainTex
: 主要纹理,默认为白色("white"
),该纹理的颜色会用作基础。
_T
: 时间变量,控制着闪烁效果的动态性。通常在渲染时会根据时间传递给着色器,范围从0到1。
_Mul
: 颜色乘法因子,用来调整图像的亮度。
_Offset
: 用于调整颜色的偏移量,影响整体颜色。
_Amp
: 振幅,控制颜色变化的强度。负值表示颜色变暗。
_Freq
: 频率,用来控制噪声的变化频率,从而影响颜色的闪烁速度。
1Shader "Nova/$VARIANT_NAME$/Blink"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Mul ("Multiplier", Float) = 1.0
8 _Offset ("Offset", Float) = 0.0
9 _Amp ("Amplitude", Float) = -0.5
10 _Freq ("Frequency", Float) = 10.0
11 }
12 SubShader
13 {
14 $VARIANT_TAGS$
15 Pass
16 {
17 CGPROGRAM
18 #pragma vertex vert
19 #pragma fragment frag
20
21 #include "UnityCG.cginc"
22 #include "Assets/Nova/CGInc/Rand.cginc"
23
24 struct appdata
25 {
26 float4 vertex : POSITION;
27 float2 uv : TEXCOORD0;
28 float4 color : COLOR;
29 };
30
31 struct v2f
32 {
33 float4 vertex : SV_POSITION;
34 float2 uv : TEXCOORD0;
35 float4 color : COLOR;
36 };
37
38 v2f vert(appdata v)
39 {
40 v2f o;
41 o.vertex = UnityObjectToClipPos(v.vertex);
42 o.uv = v.uv;
43 o.color = v.color;
44 return o;
45 }
46
47 sampler2D _MainTex;
48 float _T, _Mul, _Offset, _Amp, _Freq;
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 // 使用 tex2D(_MainTex, i.uv) 从主纹理中获取颜色值。
53 // 将顶点传递的颜色 i.color 与其相乘,应用顶点颜色。
54 // 然后,通过 _Mul 对颜色的 RGB 通道进行乘法处理,调整图像的亮度。
55 // 最后,通过 _Offset 增加颜色偏移,改变图像的亮度和颜色。
56 float4 col = tex2D(_MainTex, i.uv) * i.color;
57 col.rgb *= _Mul;
58 col.rgb += _Offset;
59
60 // noise(_Freq * _Time.y):生成一个基于时间的噪声值。
61 // _Time.y 表示自启动以来的时间(单位为秒),并且乘上了频率 _Freq 来控制噪声的变化速度。
62 // n * n 使噪声值的影响更加剧烈,生成一个强烈的闪烁效果。
63 // _Amp * n * n * _T:将噪声的振幅(_Amp)与时间 (_T) 相乘,从而控制效果随时间变化。
64 // 这里 _T 的值会根据时间或其他控制因素进行变化,通常控制动画的进度。
65 float n = noise(_Freq * _Time.y);
66 col.rgb += _Amp * n * n * _T;
67
68 // saturate(col.rgb) 确保颜色值在[0, 1]范围内,防止颜色溢出。
69 // 这是为了确保颜色值不会超出有效范围,避免出现无效的颜色值。
70 col.rgb = saturate(col.rgb);
71
72 $VARIANT_RGB$
73
74 return col;
75 }
76 ENDCG
77 }
78 }
79}
Broken TV
Broken TV 效果通常用于在游戏中表现图像破碎、扭曲、噪声等类似旧电视机的效果。它通过一些图像处理技术,例如扫描线、色差、噪声和扭曲来模拟这一效果。下面将详细解释这段代码的每个部分。
_MainTex
: 主纹理,用于显示图像,默认为白色。
_T
: 时间控制器,通常根据实际时间传递给着色器,控制动态效果的强度(例如图像的破碎程度)。
_Roll
: 这个参数控制Y轴方向上的滚动效果,可能会影响图像在竖直方向的扭曲程度。
1Shader "Nova/$VARIANT_NAME$/Broken TV"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Roll ("Y Rolling", Range(0.0, 1.0)) = 0.07
8 }
9 SubShader
10 {
11 $VARIANT_TAGS$
12 Pass
13 {
14 CGPROGRAM
15 #pragma vertex vert
16 #pragma fragment frag
17
18 #include "UnityCG.cginc"
19 #include "Assets/Nova/CGInc/Rand.cginc"
20
21 struct appdata
22 {
23 float4 vertex : POSITION;
24 float2 uv : TEXCOORD0;
25 float4 color : COLOR;
26 };
27
28 struct v2f
29 {
30 float4 vertex : SV_POSITION;
31 float2 uv : TEXCOORD0;
32 float4 color : COLOR;
33 };
34
35 v2f vert(appdata v)
36 {
37 v2f o;
38 o.vertex = UnityObjectToClipPos(v.vertex);
39 o.uv = v.uv;
40 o.color = v.color;
41 return o;
42 }
43
44 // power 函数用于生成一个非线性的时间变化
45 // 它基于输入的x(在这里是 _T)计算一个“增强”值,使得时间越大,效果越强烈。
46 float power(float x)
47 {
48 x += 1.0;
49 x *= x;
50 x *= x;
51 x -= 1.0;
52 return x;
53 }
54
55 sampler2D _MainTex;
56 float _T, _Roll;
57
58 fixed4 frag(v2f i) : SV_Target
59 {
60 // 使用前面定义的 power 函数来增强时间的影响,使得随着 _T 逐渐增大,效果会逐渐变得更强烈。
61 float2 uv = i.uv;
62 float powerT = power(_T);
63
64 // xOffset 是根据时间和纹理坐标的纵向(uv.y)生成的噪声,增加了纹理水平位置的扭曲。
65 // srand 和 snoise 都是噪声函数,srand 提供伪随机数,snoise 生成平滑的噪声。
66 // 通过这些噪声,纹理的水平坐标会出现细微的扭曲,模拟图像的破碎或滚动效果。
67 float2 ty = float2(_Time.y, uv.y * 20.0);
68 float xOffset = srand(ty) * 0.01;
69 xOffset += snoise(ty) * 0.05;
70 float x = uv.x + xOffset * _T;
71
72 // 垂直方向的偏移量 yOffset 会根据 xOffset 以及 _T 来动态变化
73 // 同时与 _Roll 参数(控制垂直滚动的强度)相乘。
74 // step 函数用来根据噪声生成的值和_Roll值决定是否进行滚动。
75 // 使用 frac(uv.y + yOffset) 来确保纵向坐标 y 在 0 到 1 之间循环,从而制造扫描线效果。
76 float yOffset = (_Time.y + xOffset) * powerT;
77 yOffset *= step(noise(_Time.y), powerT * _Roll);
78 float y = frac(uv.y + yOffset);
79
80 // 这里通过水平位移 xOffset 来分别获取红色、绿色和蓝色通道的不同位置,实现色差效应
81 // 使图像的颜色出现分离和偏移,模拟破损电视机的视觉效果。
82 // Chromatic aberration
83 float rbOffset = xOffset * 0.1 * powerT;
84 float r = tex2D(_MainTex, float2(x - rbOffset, y)).r;
85 float g = tex2D(_MainTex, float2(x, y)).g;
86 float b = tex2D(_MainTex, float2(x + rbOffset, y)).b;
87 float a = tex2D(_MainTex, float2(x, y)).a;
88 float4 col = float4(r, g, b, a);
89 col *= i.color;
90
91 // 使用正弦函数生成纵向的扫描线效果,sin 函数产生周期性的波动,模拟电视屏幕的扫描线效果
92 // _T 用来控制扫描线的强度。
93 // Scan lines
94 col.rgb -= sin((uv.y + _Time.x) * 500.0) * 0.1 * _T * col.a;
95
96 // 这部分通过随机噪声在图像上叠加雪花噪声,增加破损的杂点效果。
97 // Snow noise
98 col.rgb += srand(uv * 100.0 + _Time.y) * 0.04 * powerT * col.a;
99
100 $VARIANT_RGB$
101
102 return col;
103 }
104 ENDCG
105 }
106 }
107}
Change Texture With Fade
Change Texture With Fade 通过两个纹理(_PrimaryTex
和 _SubTex
)以及 _T
时间参数,创建了一个平滑的纹理过渡效果。_T
控制主纹理和副纹理之间的混合比例,_Offsets
控制纹理的偏移,_Color
和 _SubColor
控制两个纹理的颜色。这个效果可以用来实现纹理切换或渐变效果,常见于 UI 动画、过渡效果或动态场景切换等。
1VARIANTS: Default
2Shader "Nova/$VARIANT_NAME$/Change Texture With Fade"
3{
4 Properties
5 {
6 [HideInInspector] _MainTex ("Dummy Texture Providing Size", 2D) = "white" {}
7 [NoScaleOffset] _PrimaryTex ("Primary Texture", 2D) = "white" {}
8 [NoScaleOffset] _SubTex ("Secondary Texture", 2D) = "black" {}
9 _Offsets ("Offsets (x1, y1, x2, y2)", Vector) = (0, 0, 0, 0)
10 _Color ("Primary Texture Color", Color) = (1, 1, 1, 1)
11 _SubColor ("Secondary Texture Color", Color) = (1, 1, 1, 1)
12 _T ("Time", Range(0.0, 1.0)) = 0.0
13 }
14 SubShader
15 {
16 $VARIANT_TAGS$
17 Pass
18 {
19 CGPROGRAM
20 #pragma vertex vert
21 #pragma fragment frag
22
23 #include "UnityCG.cginc"
24
25 #define clamped2D(tex, uv) tex2D((tex), clamp((uv), 0, 1))
26
27 struct appdata
28 {
29 float4 vertex : POSITION;
30 float2 uv : TEXCOORD0;
31 };
32
33 struct v2f
34 {
35 float4 vertex : SV_POSITION;
36 float2 uv : TEXCOORD0;
37 };
38
39 v2f vert(appdata v)
40 {
41 v2f o;
42 o.vertex = UnityObjectToClipPos(v.vertex);
43 o.uv = v.uv;
44 return o;
45 }
46
47 sampler2D _PrimaryTex, _SubTex;
48 float4 _Offsets, _Color, _SubColor;
49 float _T;
50
51 fixed4 frag(v2f i) : SV_Target
52 {
53 // lerp 函数用于计算主纹理和副纹理之间的插值
54 // 在这里,lerp 的第一个参数是主纹理,第二个参数是副纹理,第三个参数是时间(_T)
55 // _T 控制主纹理和副纹理的权重,_T = 0 时显示主纹理,_T = 1 时显示副纹理
56 // 通过插值,_T 的值控制纹理的渐变效果
57
58 // clamped2D(tex, uv) 是一个宏,用于从纹理中采样颜色
59 // 同时确保 UV 坐标被限制在 [0, 1] 范围内,以防止越界
60 // 它通过 clamp(uv, 0, 1) 实现,保证即使 UV 坐标超过边界,也会被“修剪”到有效范围内。
61 float4 col = lerp(
62 // 对主纹理进行采样,并根据 _Offsets.xy 对 UV 坐标进行偏移
63 clamped2D(_PrimaryTex, i.uv - _Offsets.xy) * _Color,
64 // 对副纹理进行采样,并根据 _Offsets.zw 对 UV 坐标进行偏移
65 clamped2D(_SubTex, i.uv - _Offsets.zw) * _SubColor,
66 _T
67 );
68
69 $VARIANT_RGB$
70
71 return col;
72 }
73 ENDCG
74 }
75 }
76}
Color
这段着色器代码实现了一个基于颜色操作的效果,允许用户通过时间参数(_T
)动态调整纹理的颜色。
1Shader "Nova/$VARIANT_NAME$/Color"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _ColorMul ("Color Multiplier", Color) = (1, 1, 1, 1)
8 _ColorAdd ("Color Offset", Vector) = (0, 0, 0, 0)
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20
21 $DEF_IADD_RGBA$
22
23 struct appdata
24 {
25 float4 vertex : POSITION;
26 float2 uv : TEXCOORD0;
27 float4 color : COLOR;
28 };
29
30 struct v2f
31 {
32 float4 vertex : SV_POSITION;
33 float2 uv : TEXCOORD0;
34 float4 color : COLOR;
35 };
36
37 v2f vert(appdata v)
38 {
39 v2f o;
40 o.vertex = UnityObjectToClipPos(v.vertex);
41 o.uv = v.uv;
42 o.color = v.color;
43 return o;
44 }
45
46 sampler2D _MainTex;
47 float _T;
48 float4 _ColorMul, _ColorAdd;
49
50 // 使用 lerp 在原始颜色(col)和调整后的颜色(col2)之间进行线性插值
51 // 权重由 _T 控制:
52 // _T = 0: 输出为原始颜色。
53 // _T = 1: 输出为调整后的颜色。
54 fixed4 frag(v2f i) : SV_Target
55 {
56 float4 col = tex2D(_MainTex, i.uv) * i.color;
57 float4 col2 = col * _ColorMul;
58 IADD_RGBA(col2, _ColorAdd)
59 col = lerp(col, col2, _T);
60
61 $VARIANT_RGB$
62
63 return col;
64 }
65 ENDCG
66 }
67 }
68}
Colorless
这段着色器代码实现了一个“去色”(Colorless)效果,它将颜色信息减少,突出明度,并通过噪声引入动态扰动。这种效果可以应用于场景渲染中的特殊视觉风格,例如模拟老旧电视或用于创造复古效果。以下是详细分析:
1VARIANTS: PP
2Shader "Nova/$VARIANT_NAME$/Colorless"
3{
4 Properties
5 {
6 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
7 _T ("Time", Range(0.0, 1.0)) = 0.0
8 }
9 SubShader
10 {
11 $VARIANT_TAGS$
12 Pass
13 {
14 CGPROGRAM
15 #pragma vertex vert
16 #pragma fragment frag
17
18 #include "UnityCG.cginc"
19 #include "Assets/Nova/CGInc/Rand.cginc"
20
21 struct appdata
22 {
23 float4 vertex : POSITION;
24 float2 uv : TEXCOORD0;
25 float4 color : COLOR;
26 };
27
28 struct v2f
29 {
30 float4 vertex : SV_POSITION;
31 float2 uv : TEXCOORD0;
32 float4 color : COLOR;
33 };
34
35 v2f vert(appdata v)
36 {
37 v2f o;
38 o.vertex = UnityObjectToClipPos(v.vertex);
39 o.uv = v.uv;
40 o.color = v.color;
41 return o;
42 }
43
44 sampler2D _MainTex;
45 float _T;
46
47 fixed4 frag(v2f i) : SV_Target
48 {
49 float4 col = tex2D(_MainTex, i.uv);
50
51 // 使用简单噪声函数 snoise2 生成一个二维扰动向量。
52 // i.uv * 10.0: 提高噪声频率,使得扰动更细致。
53 // _Time.y: 动态时间,用于生成随时间变化的噪声。
54 float2 v = 0.001 * snoise2(float3(i.uv * 10.0, _Time.y));
55 // 将颜色转换为灰度值(明度)
56 float gray = Luminance(col.rgb);
57
58 // 使用灰度平方增加对高亮区域的扰动强度。
59 // 进一步平方增强对高亮区域的区分度。
60 // 乘以 _T,允许通过时间参数控制扰动效果。
61 float mask = gray * gray;
62 mask = 2.0 * mask * mask * _T;
63 v *= mask;
64
65 // 根据掩码调整扰动向量 v 的大小。
66 // 在红色通道上向 +v 偏移采样,在绿色通道上向 -v 偏移采样。
67 // 蓝色通道保持不变,模拟“去色”效果。
68 col.r = tex2D(_MainTex, i.uv + v).r;
69 col.g = tex2D(_MainTex, i.uv - v).g;
70
71 col *= i.color;
72
73 $VARIANT_RGB$
74
75 return col;
76 }
77 ENDCG
78 }
79 }
80}
Fade
这里需要总结一下各个 Fade 之间的差异
Fade
这段着色器实现了一个渐变过渡效果(Fade),允许主纹理与次纹理之间进行平滑过渡,并使用遮罩图(Mask)定义过渡区域和效果模糊程度。以下是详细分析:
- 场景过渡效果:
- 场景切换时实现从一种纹理渐变到另一种纹理的视觉过渡。
- 视觉特效:
- 添加局部变化效果,如图像的动态擦除或显现。
- 高亮或隐蔽区域:
- 使用遮罩图动态显示或隐藏纹理内容。
1Shader "Nova/$VARIANT_NAME$/Fade"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 [NoScaleOffset] _SubTex ("Second Texture", 2D) = "black" {}
7 _SubColor ("Second Texture Color", Color) = (1, 1, 1, 1)
8 _T ("Time", Range(0.0, 1.0)) = 0.0
9 _Mask ("Mask", 2D) = "white" {}
10 _Vague ("Vagueness", Range(0.0, 0.5)) = 0.25
11 _Offset ("Main Texture Luminosity Offset", Float) = 0.0
12 _InvertMask ("Invert Mask", Float) = 0.0
13 }
14 SubShader
15 {
16 $VARIANT_TAGS$
17 Pass
18 {
19 CGPROGRAM
20 #pragma vertex vert
21 #pragma fragment frag
22
23 #include "UnityCG.cginc"
24
25 struct appdata
26 {
27 float4 vertex : POSITION;
28 float2 uv : TEXCOORD0;
29 float4 color : COLOR;
30 };
31
32 struct v2f
33 {
34 float4 vertex : SV_POSITION;
35 float2 uv : TEXCOORD0;
36 float2 uvMask : TEXCOORD1;
37 float4 color : COLOR;
38 };
39
40 sampler2D _Mask;
41 float4 _Mask_ST;
42
43 v2f vert(appdata v)
44 {
45 v2f o;
46 o.vertex = UnityObjectToClipPos(v.vertex);
47 o.uv = v.uv;
48 o.uvMask = TRANSFORM_TEX(v.uv, _Mask);
49 o.color = v.color;
50 return o;
51 }
52
53 sampler2D _MainTex, _SubTex;
54 float4 _SubColor;
55 float _T, _Vague, _Offset, _InvertMask;
56
57 fixed4 frag(v2f i) : SV_Target
58 {
59 // 使用 i.uvMask 从 _Mask 中采样红色通道,获取遮罩值
60 // 当 _InvertMask 为 1 时,遮罩值将被反转(1 - t0)
61 float t0 = tex2D(_Mask, i.uvMask).r;
62 t0 = _InvertMask + t0 - 2 * _InvertMask * t0;
63 // 根据 _Vague 参数,缩放遮罩值范围以控制过渡的边界模糊程度
64 t0 = t0 * (1.0 - 2.0 * _Vague) + _Vague;
65 // 使用 smoothstep 函数生成平滑的过渡边界
66 // slope 控制边界梯度,基于 _Vague 的值动态调整。
67 float slope = 0.5 / (_Vague + 0.001);
68 float mask = smoothstep(0.0, 1.0, 0.5 + slope * (_T - t0));
69
70 // 从主纹理采样颜色
71 // 根据 _T 和 _Offset 参数动态调整亮度,并使用 saturate 限制颜色范围到 [0, 1]
72 float4 col = tex2D(_MainTex, i.uv) * i.color;
73 col.rgb += _Offset * _T;
74 col.rgb = saturate(col.rgb);
75
76 // 从次纹理采样颜色,并应用颜色乘数 _SubColor
77 // 使用 lerp 函数在主纹理与次纹理之间进行线性插值,权重由 mask 决定
78 float4 col2 = tex2D(_SubTex, i.uv) * _SubColor;
79 col = lerp(col, col2, mask);
80
81 $VARIANT_RGB$
82
83 return col;
84 }
85 ENDCG
86 }
87 }
88}
Fade Global
从应用场景看:
Fade
着色器:用于局部渐变效果,支持基于遮罩(Mask)的渐变控制。适合需要通过遮罩纹理逐步显现或隐藏的效果,如局部区域的纹理切换或动态显示。
Fade Global
着色器:用于全局渐变,直接在两张纹理之间进行线性插值,无需遮罩纹理。适合全屏或整块纹理的渐变切换,如 UI 元素整体过渡或全屏特效
渐变控制方式看:
Fade
着色器:使用了遮罩纹理(_Mask
)来控制渐变的范围和形状。提供模糊程度(_Vague
)和遮罩反转(_InvertMask
)参数,用于灵活调整渐变的细节。
Fade Global
着色器:通过 _T
参数直接控制两张纹理之间的全局线性插值,无需额外的遮罩纹理。偏移参数(_Offsets
)可以调整两张纹理的 UV 坐标,实现基础的纹理偏移效果。
参数和灵活性看:
Fade
着色器:参数丰富,适合复杂的局部渐变效果:
_Mask
:遮罩纹理,决定渐变的范围。_Vague
:模糊范围,影响过渡边缘的柔和程度。_InvertMask
:控制遮罩的反转,灵活改变渐变的方向。
Fade Global
着色器:参数简单,专注于全局过渡:
_T
:用于控制全局混合的时间参数。_Offsets
:简单的 UV 偏移调整,用于动态效果。
1VARIANTS: Premul
2Shader "Nova/$VARIANT_NAME$/Fade Global"
3{
4 Properties
5 {
6 [HideInInspector] _MainTex ("Dummy Texture Providing Size", 2D) = "white" {}
7 [NoScaleOffset] _PrimaryTex ("Primary Texture", 2D) = "clear" {}
8 [NoScaleOffset] _SubTex ("Secondary Texture", 2D) = "clear" {}
9 _Offsets ("Offsets (x1, y1, x2, y2)", Vector) = (0, 0, 0, 0)
10 _Color ("Primary Texture Color", Color) = (1, 1, 1, 1)
11 _SubColor ("Secondary Texture Color", Color) = (1, 1, 1, 1)
12 _T ("Time", Range(0.0, 1.0)) = 0.0
13 }
14 SubShader
15 {
16 $VARIANT_TAGS$
17 Pass
18 {
19 CGPROGRAM
20 #pragma vertex vert
21 #pragma fragment frag
22
23 #include "UnityCG.cginc"
24
25 #define clamped2D(tex, uv) tex2D((tex), clamp((uv), 0, 1))
26
27 struct appdata
28 {
29 float4 vertex : POSITION;
30 float2 uv : TEXCOORD0;
31 };
32
33 struct v2f
34 {
35 float4 vertex : SV_POSITION;
36 float2 uv : TEXCOORD0;
37 };
38
39 v2f vert(appdata v)
40 {
41 v2f o;
42 o.vertex = UnityObjectToClipPos(v.vertex);
43 o.uv = v.uv;
44 return o;
45 }
46
47 sampler2D _PrimaryTex, _SubTex;
48 float4 _Offsets, _Color, _SubColor;
49 float _T;
50
51 fixed4 frag(v2f i) : SV_Target
52 {
53 // 实现较为简单,直接通过 lerp 函数进行两张纹理的线性插值:
54 float4 col = lerp(
55 clamped2D(_PrimaryTex, i.uv - _Offsets.xy) * _Color,
56 clamped2D(_SubTex, i.uv - _Offsets.zw) * _SubColor,
57 _T
58 );
59
60 $VARIANT_RGB$
61
62 return col;
63 }
64 ENDCG
65 }
66 }
67}
Fade Radial Blur
Fade Radial Blur 着色器是在之前渐变效果基础上,加入了径向模糊(Radial Blur)和动态缩放(Zoom)效果的增强版。以下是对其功能、特点和逻辑的详细分析,并与之前的 Fade 和 Fade Global 着色器进行对比。
虽然 Fade Radial Blur 在性能上比其他 Fade
系列着色器开销更大,但效果更具视觉冲击力,适用于对性能要求较低的场景,如过场动画或静态画面展示切换。
1Shader "Nova/$VARIANT_NAME$/Fade Radial Blur"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 [NoScaleOffset] _SubTex ("Second Texture", 2D) = "black" {}
7 _SubColor ("Second Texture Color", Color) = (1, 1, 1, 1)
8 _T ("Time", Range(0.0, 1.0)) = 0.0
9 _Size ("Size", Float) = 1.0
10 _Zoom ("Zoom", Float) = 0.5
11 _Dir ("Direction", Float) = 0.0
12 }
13 SubShader
14 {
15 $VARIANT_TAGS$
16 Pass
17 {
18 CGPROGRAM
19 #pragma vertex vert
20 #pragma fragment frag
21
22 #include "UnityCG.cginc"
23 #include "Assets/Nova/CGInc/Blur.cginc"
24
25 struct appdata
26 {
27 float4 vertex : POSITION;
28 float2 uv : TEXCOORD0;
29 float4 color : COLOR;
30 };
31
32 struct v2f
33 {
34 float4 vertex : SV_POSITION;
35 float2 uv : TEXCOORD0;
36 float4 color : COLOR;
37 };
38
39 v2f vert(appdata v)
40 {
41 v2f o;
42 o.vertex = UnityObjectToClipPos(v.vertex);
43 o.uv = v.uv;
44 o.color = v.color;
45 return o;
46 }
47
48 sampler2D _MainTex, _SubTex;
49 float4 _MainTex_TexelSize, _SubColor;
50 float _T, _Size, _Zoom, _Dir;
51 $DEF_GSCALE$
52
53 fixed4 frag(v2f i) : SV_Target
54 {
55 // 通过 uvShift 计算出以中心点 (0.5, 0.5) 为参考的偏移量
56 float2 uvShift = i.uv - 0.5;
57 // 根据缩放参数 _Zoom 和渐变时间 _T,动态计算放缩后的 UV 坐标
58 float2 uvZoom = uvShift * (1.0 - _Zoom * _T * _Dir) + 0.5;
59 // 第二纹理使用反向时间参数(1.0 - _T)计算放缩 UV
60 float2 uvZoom2 = uvShift * (1.0 - _Zoom * (1.0 - _T) * (1.0 - _Dir)) + 0.5;
61 // 使用模糊函数 tex2DMotionBlur 为主纹理和次纹理分别应用径向模糊
62 // tex2DMotionBlur 是一个自定义函数,用于模拟径向模糊效果
63 float4 col = tex2DMotionBlur(_MainTex,
64 _MainTex_TexelSize * $GSCALE$, uvZoom,
65 uvShift * _Size * _T) * i.color;
66 float4 col2 = tex2DMotionBlur(_SubTex,
67 _MainTex_TexelSize * $GSCALE$, uvZoom2,
68 uvShift * _Size * (1.0 - _T)) * _SubColor;
69 // 最终对主纹理和次纹理进行插值,权重由 _T 控制
70 col = lerp(col, col2, _T);
71
72 $VARIANT_RGB$
73
74 return col;
75 }
76 ENDCG
77 }
78 }
79}
Final Blit
Final Blit 着色器的作用是在最终渲染阶段对画面进行屏幕空间调整,同时处理超出纹理范围的区域(即边缘部分)。这是一个用于图像后期处理的着色器,常用于对完整画面的最后渲染阶段进行自定义操作。
1VARIANTS: Premul
2Shader "Nova/$VARIANT_NAME$/Final Blit"
3{
4 Properties
5 {
6 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
7 _Color ("Margin Color", Color) = (0, 0, 0, 1)
8 }
9 SubShader
10 {
11 $VARIANT_TAGS$
12 Pass
13 {
14 CGPROGRAM
15 #pragma vertex vert
16 #pragma fragment frag
17
18 #include "UnityCG.cginc"
19
20 struct appdata
21 {
22 float4 vertex : POSITION;
23 float2 uv : TEXCOORD0;
24 };
25
26 struct v2f
27 {
28 float4 vertex : SV_POSITION;
29 float2 uv : TEXCOORD0;
30 };
31
32 v2f vert(appdata v)
33 {
34 v2f o;
35 o.vertex = UnityObjectToClipPos(v.vertex);
36 o.uv = v.uv;
37 return o;
38 }
39
40 sampler2D _MainTex;
41 float4 _Color;
42 float _GW, _GH;
43
44 fixed4 frag(v2f i) : SV_Target
45 {
46 float2 pivot = float2(0.5, 0.5);
47 float2 uv = (i.uv - pivot) / float2(_GW, _GH) * _ScreenParams.xy + pivot;
48 // eps 通常是指一个非常小的数值(epsilon),用于避免计算误差或浮点数比较问题。
49 float eps = 1e-6;
50 float4 col;
51
52 if (uv.x > -eps && uv.x < 1 + eps && uv.y > -eps && uv.y < 1 + eps)
53 {
54 col = tex2D(_MainTex, clamp(uv, 0, 1));
55 }
56 else
57 {
58 col = _Color;
59 }
60
61 $VARIANT_RGB$
62
63 return col;
64 }
65 ENDCG
66 }
67 }
68}
Flip Grid
着色器实现了一个网格翻转动画效果,其中两个纹理在动画过程中逐步翻转切换,表现为网格单元的动态过渡效果。以下是着色器的详细解读:
顶点着色器 vert
输入:appdata 包含顶点位置、UV 和颜色信息。
输出:v2f 包含屏幕空间顶点位置、UV、颜色和网格信息。
- 计算了网格的对角线总数 gridInfo.x。
- 计算每个单元格的翻转起始时间间隔 gridInfo.y。
- 储存翻转动画的半段时间 gridInfo.z。
片段着色器 frag 实现了动画翻转逻辑,分为三个阶段:
未到翻转时间:网格的翻转还未开始,显示主纹理 _MainTex。
翻转中:动态计算单元格内部的翻转动画。使用 smoothStepIn 实现平滑动画,计算旋转角度 theta。根据 theta 计算像素位置在旋转过程中是否落在当前单元格内,并选择合适的纹理。
翻转完成:翻转结束,显示副纹理 _SubTex。
1Shader "Nova/$VARIANT_NAME$/Flip Grid"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 [NoScaleOffset] _SubTex ("Second Texture", 2D) = "black" {}
7 _SubColor ("Second Texture Color", Color) = (1, 1, 1, 1)
8 _T ("Time", Range(0.0, 1.0)) = 0.0
9 _BackColor ("Background Color", Color) = (0, 0, 0, 1)
10 _GridSize ("Grid Size", Float) = 64.0
11 _FlipDuration ("Flip Duration", Range(0.0, 1.0)) = 0.5
12 }
13 SubShader
14 {
15 $VARIANT_TAGS$
16 Pass
17 {
18 CGPROGRAM
19 #pragma vertex vert
20 #pragma fragment frag
21
22 #include "UnityCG.cginc"
23
24 #define PI 3.1415927
25 #define smoothStepIn(x) ((x) * (x) * (2.0 - x))
26
27 struct appdata
28 {
29 float4 vertex : POSITION;
30 float2 uv : TEXCOORD0;
31 float4 color : COLOR;
32 };
33
34 struct v2f
35 {
36 float4 vertex : SV_POSITION;
37 float2 uv : TEXCOORD0;
38 float4 color : COLOR;
39 float3 gridInfo : TEXCOORD1; // x: max diag num; y: flip start time interval; z: half duration
40 };
41
42 sampler2D _MainTex, _SubTex;
43 float4 _MainTex_TexelSize, _SubColor, _BackColor;
44 float _T, _GridSize, _FlipDuration;
45
46 v2f vert(appdata v)
47 {
48 v2f o;
49 o.vertex = UnityObjectToClipPos(v.vertex);
50 o.uv = v.uv;
51 o.color = v.color;
52 float2 gridIndex = _MainTex_TexelSize.zw / _GridSize;
53 o.gridInfo.x = round(gridIndex.x) + round(gridIndex.y);
54 o.gridInfo.y = (1.0 - _FlipDuration) / o.gridInfo.x;
55 o.gridInfo.z = _FlipDuration / 2.0;
56 return o;
57 }
58
59 fixed4 frag(v2f i) : SV_Target
60 {
61 float2 pixelPos = i.uv * _MainTex_TexelSize.zw / _GridSize;
62 float2 gridIndex = floor(pixelPos); // Left bottom point of grid
63 float gridDiagIndex = gridIndex.x + gridIndex.y;
64 float flipStartTime = gridDiagIndex * i.gridInfo.y;
65 float flipEndTime = flipStartTime + _FlipDuration;
66
67 float4 col = _BackColor;
68
69 if (flipStartTime > _T)
70 {
71 // Transition has not arrive here
72 col = tex2D(_MainTex, i.uv) * i.color;
73 }
74 else if (flipEndTime < _T)
75 {
76 // Transition finished
77 col = tex2D(_SubTex, i.uv) * _SubColor;
78 }
79 else
80 {
81 float2 p = frac(pixelPos);
82 float2 intersect = float2(1 + p.x - p.y, 1 - p.x + p.y) / 2.0;
83 float deltaTime = _T - flipStartTime;
84 if (deltaTime < i.gridInfo.z)
85 {
86 float theta = PI / 2.0 * smoothStepIn(deltaTime / i.gridInfo.z);
87 float dis = (1 - p.x - p.y) * 0.5 / (cos(theta) + 0.001);
88 float2 q = intersect - dis;
89 if (q.x < 0.0 || q.y < 0.0 || q.x > 1.0 || q.y > 1.0)
90 {
91 return col;
92 }
93 float2 realUV = (gridIndex + q) * _GridSize * _MainTex_TexelSize.xy;
94 col = tex2D(_MainTex, realUV) * i.color;
95 }
96 else
97 {
98 float theta = PI / 2.0 * smoothStepIn(
99 (_FlipDuration - deltaTime) / i.gridInfo.z);
100 float dis = (1 - p.x - p.y) * 0.5 / (cos(theta) + 0.001);
101 float2 q = intersect - dis;
102 if (q.x < 0.0 || q.y < 0.0 || q.x > 1.0 || q.y > 1.0)
103 {
104 return col;
105 }
106 float2 realUV = (gridIndex + q) * _GridSize * _MainTex_TexelSize.xy;
107 col = tex2D(_SubTex, realUV) * _SubColor;
108 }
109 }
110
111 $VARIANT_RGB$
112
113 return col;
114 }
115 ENDCG
116 }
117 }
118}
Gaussian Blur
着色器实现了高斯模糊(Gaussian Blur)的效果,并且可以通过一些参数动态调整模糊程度、偏移值以及过渡效果。
1Shader "Nova/$VARIANT_NAME$/Gaussian Blur"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 1.0
8 _Offset ("Offset", Float) = 0.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Blur.cginc"
21
22 struct appdata
23 {
24 float4 vertex : POSITION;
25 float2 uv : TEXCOORD0;
26 float4 color : COLOR;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 float4 color : COLOR;
34 };
35
36 v2f vert(appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41 o.color = v.color;
42 return o;
43 }
44
45 sampler2D _MainTex;
46 float4 _MainTex_TexelSize;
47 float _T, _Size, _Offset;
48 $DEF_GSCALE$
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 float4 col = tex2DGaussianBlur(_MainTex,
53 _MainTex_TexelSize * $GSCALE$,
54 i.uv, _Size * _T);
55 col *= i.color;
56 col.rgb += _Offset * _T;
57 col.rgb = saturate(col.rgb);
58
59 $VARIANT_RGB$
60
61 return col;
62 }
63 ENDCG
64 }
65 }
66}
Gitch
着色器实现了一个 Glitch 特效,模仿了电子设备显示错误或信号干扰时的视觉效果。它通过噪声函数和动态 UV 偏移来创建这些效果。
1Shader "Nova/$VARIANT_NAME$/Glitch"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 }
8 SubShader
9 {
10 $VARIANT_TAGS$
11 Pass
12 {
13 CGPROGRAM
14 #pragma vertex vert
15 #pragma fragment frag
16
17 #include "UnityCG.cginc"
18 #include "Assets/Nova/CGInc/Rand.cginc"
19
20 struct appdata
21 {
22 float4 vertex : POSITION;
23 float2 uv : TEXCOORD0;
24 float4 color : COLOR;
25 };
26
27 struct v2f
28 {
29 float4 vertex : SV_POSITION;
30 float2 uv : TEXCOORD0;
31 float4 color : COLOR;
32 };
33
34 v2f vert(appdata v)
35 {
36 v2f o;
37 o.vertex = UnityObjectToClipPos(v.vertex);
38 o.uv = v.uv;
39 o.color = v.color;
40 return o;
41 }
42
43 float blockNoise(float2 uv, float freqX, float freqY, float freqT)
44 {
45 float a = floor(noise(_Time.y * freqT) * 100.0);
46 a = floor(noise(uv.y * freqY + a) * 100.0);
47 a = floor(noise(uv.x * freqX + a) * 100.0);
48 a = floor(noise(uv.y * freqY + a) * 100.0);
49 a = rand(a);
50 return a;
51 }
52
53 float power(float x)
54 {
55 x += 1.0;
56 x *= x;
57 x *= x;
58 x -= 1.0;
59 return x;
60 }
61
62 float blockOffsetPass(float2 uv, float freqX, float freqY, float freqT,
63 float threshold, float value)
64 {
65 float blockOffsetOn = blockNoise(uv, freqX, freqY, freqT);
66 if (blockOffsetOn < threshold)
67 {
68 return value;
69 }
70 return 0.0;
71 }
72
73 sampler2D _MainTex;
74 float _T;
75
76 fixed4 frag(v2f i) : SV_Target
77 {
78 float2 uv = i.uv;
79 float powerT = power(_T);
80
81 // 计算是否启用偏移,如果噪声值小于 _T,则启用偏移
82 float uvOffsetOn = blockNoise(uv, 0.2, 0.2, 0.05);
83 float2 uvOffset = float2(0.0, 0.0);
84 if (uvOffsetOn < _T)
85 {
86 // 偏移量基于噪声生成,模拟随机抖动
87 uvOffset.x = srand(uvOffsetOn) * 0.1;
88 uvOffset.y = srand(uvOffset.x) * 0.1;
89 }
90 // 应用偏移到 UV 坐标
91 float2 uv2 = uv + uvOffset;
92
93 // rbOffset 用于后续的颜色错位计算
94 float rbOffsetOn = blockNoise(uv, 0.1, 0.1, 0.5);
95 float2 rbOffset = float2(0.0, 0.0);
96 if (rbOffsetOn < 0.2 * _T)
97 {
98 rbOffset.x = srand(rbOffsetOn) * 0.1;
99 rbOffset.y = srand(rbOffset.x) * 0.1;
100 }
101
102 // 为了增强 Glitch 效果,着色器对 RGB 三个通道应用不同的 UV 偏移
103 // 这种处理方式模仿了图像的 RGB 分量发生错位的视觉现象
104 // R 通道:使用负偏移
105 // G 通道:无偏移
106 // B 通道:使用正偏移
107 float r = tex2D(_MainTex, uv2 - rbOffset).r;
108 float g = tex2D(_MainTex, uv2).g;
109 float b = tex2D(_MainTex, uv2 + rbOffset).b;
110 float a = tex2D(_MainTex, uv2).a;
111 float4 col = float4(r, g, b, a);
112 col *= i.color;
113
114 // blockOffsetPass 函数实现了局部的块状干扰
115 // 使用 blockNoise 判断当前区域是否启用块状干扰
116 // 如果启用,返回指定的干扰值(如 -0.5 或 1.0)
117 // 最终影响 col.rgb,形成随机的局部亮度或颜色突变
118 float blockOffset = 0.0;
119 blockOffset += blockOffsetPass(uv, 0.5, 5.0, 0.1, 0.06 * powerT, -0.5);
120 blockOffset += blockOffsetPass(uv, 2.0, 4.0, 0.1, 0.002 * powerT, 1.0);
121 col.rgb += blockOffset;
122 // 使用 saturate 限制 RGB 值范围,防止因偏移和干扰导致的颜色溢出或显示异常
123 col.rgb = saturate(col.rgb);
124
125 $VARIANT_RGB$
126
127 return col;
128 }
129 ENDCG
130 }
131 }
132}
Glow
这段代码实现了一个简单的 Glow(辉光) 特效的 Shader,用于在 Unity 中制作具有发光效果的材质。我们可以从多个方面来理解这个 Shader 的实现,包括它的结构、如何处理顶点数据、如何实现辉光效果、以及如何调整和增强这种效果。下面我会详细讲解每个部分的功能。
1Shader "Nova/$VARIANT_NAME$/Glow"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 1.0
8 _Strength ("Strength", Float) = 1.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Blur.cginc"
21
22 struct appdata
23 {
24 float4 vertex : POSITION;
25 float2 uv : TEXCOORD0;
26 float4 color : COLOR;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 float4 color : COLOR;
34 };
35
36 v2f vert(appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41 o.color = v.color;
42 return o;
43 }
44
45 sampler2D _MainTex;
46 float4 _MainTex_TexelSize;
47 float _T, _Size, _Strength;
48 $DEF_GSCALE$
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 // 1. 从主纹理采样颜色
53 float4 col = tex2D(_MainTex, i.uv) * i.color;
54
55 // 2. 计算辉光效果,应用高斯模糊
56 float3 glow = tex2DGaussianBlur(_MainTex,
57 _MainTex_TexelSize * $GSCALE$,
58 i.uv,
59 _Size * _T).rgb * i.color.rgb;
60 // 3. 强化辉光效果的亮度
61 glow *= glow;
62 glow *= glow;
63
64 // 4. 根据时间和强度调整辉光的最终亮度
65 glow *= _Strength * _T;
66 // 5. 保证辉光的亮度值在 [0, 1] 范围内
67 glow = saturate(glow);
68
69 // 6. 将辉光与原始颜色合并
70 col.rgb = col.rgb + glow - col.rgb * glow;
71
72 $VARIANT_RGB$
73 // 8. 返回最终的颜色值
74 return col;
75 }
76 ENDCG
77 }
78 }
79}
Gray Wave
Gray Wave 的 Shader,它根据噪声(noise)和时间控制来生成一种灰度波浪效果。下面我们逐行解析这个 Shader,理解每个部分的作用。
1Shader "Nova/$VARIANT_NAME$/Gray Wave"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Freq ("Frequency", Float) = 0.5
8 _Size ("Size", Float) = 50.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Rand.cginc"
21
22 struct appdata
23 {
24 float4 vertex : POSITION;
25 float2 uv : TEXCOORD0;
26 float4 color : COLOR;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 float4 color : COLOR;
34 };
35
36 v2f vert(appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41 o.color = v.color;
42 return o;
43 }
44
45 sampler2D _MainTex;
46 float _T, _Freq, _Size;
47
48 fixed4 frag(v2f i) : SV_Target
49 {
50 // 1. 从主纹理采样颜色
51 float4 col = tex2D(_MainTex, i.uv) * i.color;
52
53 // 2. 生成噪声并调整颜色
54 float n = noise(_Freq * _Time.y);
55
56 // 3. 计算灰度波浪效果
57 float3 delta = col.rgb - n;
58 col.rgb /= (1.0 + _Size * delta * delta);
59
60 $VARIANT_RGB$
61
62 // 5. 返回最终的颜色
63 return col;
64 }
65 ENDCG
66 }
67 }
68}
Kaleidoscope
这个 Shader 实现了一个 Kaleidoscope(万花筒) 效果,使用了纹理的重复、旋转和渐变过渡来模拟万花筒的视觉效果。它根据输入的纹理和时间参数,产生一种类似万花筒镜面反射的效果,使得纹理看起来像是反射在多个镜面上的样子。
万花筒(Kaleidoscope) 效果,主要通过以下几个步骤来实现:
- 对称与反射:通过对纹理坐标的操作,使得纹理重复反射,模拟镜面效果。
- 旋转:使用时间和正弦余弦函数控制每次反射时的旋转,模拟动态的万花筒效果。
- 平滑过渡:使用时间 (_T) 进行插值,以实现平滑的效果过渡。
1Shader "Nova/$VARIANT_NAME$/Kaleido"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Repeat ("Repeat", Float) = 8.0
8 _Freq ("Frequency", Float) = 1.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20
21 struct appdata
22 {
23 float4 vertex : POSITION;
24 float2 uv : TEXCOORD0;
25 float4 color : COLOR;
26 };
27
28 struct v2f
29 {
30 float4 vertex : SV_POSITION;
31 float2 uv : TEXCOORD0;
32 float4 color : COLOR;
33 float2 cosSinTime : TEXCOORD1;
34 };
35
36 float _Repeat;
37
38 v2f vert(appdata v)
39 {
40 v2f o;
41 o.vertex = UnityObjectToClipPos(v.vertex);
42 o.uv = v.uv;
43 o.color = v.color;
44 float t = _Time.y / _Repeat;
45 // 这行代码计算时间(_Time.y)的正弦和余弦值,并将其存储在 cosSinTime 中
46 o.cosSinTime = float2(cos(t), sin(t));
47 return o;
48 }
49
50 sampler2D _MainTex;
51 float _T, _Freq;
52
53 fixed4 frag(v2f i) : SV_Target
54 {
55 // 从主纹理采样颜色值
56 float4 col = tex2D(_MainTex, i.uv);
57
58 // 将纹理坐标 uv 从 [0, 1] 范围映射到 [-0.5, 0.5] 范围,以便后续进行对称操作
59 float2 uv = i.uv - 0.5;
60 // 使用循环实现镜面反射的重复效果
61 // _Repeat 控制镜面反射的数量
62 // 每次迭代中,都会对 uv 进行对称操作,从而实现类似万花筒的镜像效果。
63 for (int repeat = 0; repeat < _Repeat; ++repeat)
64 {
65 // 将 uv 转换为绝对值,产生对称效果,并且减少一些偏移量以调整对称的位置。
66 uv = abs(uv) - 0.25;
67 // 通过交换 x 和 y 分量并应用符号函数(sign),进一步产生对称效果
68 uv *= sign(uv + uv.yx);
69 // 根据 cosSinTime 对 uv 进行旋转,产生旋转效果,从而实现万花筒效果的视觉旋转
70 uv = i.cosSinTime.x * uv + i.cosSinTime.y * uv.yx * float2(1.0, -1.0);
71 }
72 // 将 uv 坐标重新映射回 [0, 1] 范围。
73 uv += 0.5;
74 // 从调整后的 uv 坐标中再次从纹理采样颜色 col2
75 float4 col2 = tex2D(_MainTex, uv);
76
77 // 使用 lerp 函数(线性插值)在原始颜色 col 和新采样的颜色 col2 之间进行平滑过渡
78 // 过渡程度由 _T 控制。同时,乘以顶点的颜色 i.color,以便影响最终颜色
79 col = lerp(col, col2, _T) * i.color;
80
81 $VARIANT_RGB$
82
83 return col;
84 }
85 ENDCG
86 }
87 }
88}
Lens Blur
Lens Blur(镜头模糊) 效果,它通过将模糊效果应用于纹理,根据时间和参数来模拟镜头焦外模糊的视觉效果。我们逐行解析这个 Shader,理解每个部分的作用。
1Shader "Nova/$VARIANT_NAME$/Lens Blur"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 1.0
8 _Offset ("Offset", Float) = 0.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Blur.cginc"
21
22 struct appdata
23 {
24 float4 vertex : POSITION;
25 float2 uv : TEXCOORD0;
26 float4 color : COLOR;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 float4 color : COLOR;
34 };
35
36 v2f vert(appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41 o.color = v.color;
42 return o;
43 }
44
45 sampler2D _MainTex;
46 float4 _MainTex_TexelSize;
47 float _T, _Size, _Offset;
48 $DEF_GSCALE$
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 // 这是一个自定义的模糊函数(定义在 Blur.cginc 中),其作用是对纹理应用镜头模糊效果
53 float4 col = tex2DLensBlur(_MainTex,
54 _MainTex_TexelSize * $GSCALE$,
55 i.uv, _Size * _T);
56 col *= i.color;
57 col.rgb += _Offset * _T;
58 col.rgb = saturate(col.rgb);
59
60 $VARIANT_RGB$
61
62 return col;
63 }
64 ENDCG
65 }
66 }
67}
Masked Mosaic
Masked Mosaic(带遮罩的马赛克)效果,它结合了纹理、时间、遮罩以及马赛克效果,通过对像素进行不同强度的采样,来模拟马赛克效果,同时使用遮罩来控制不同区域的效果强度。
- 马赛克效果:通过将纹理坐标对齐到固定的块大小(
_Size
),并通过随机偏移(srand2)使每个马赛克块有所变化,模拟马赛克效果。 - 遮罩控制:通过遮罩纹理(
_Mask
)控制哪些区域应用马赛克效果,并通过_T
参数动态调整遮罩的强度。 - 动态变化:时间参数
_T
控制遮罩效果的动态变化,使得马赛克效果可以随着时间变化
1Shader "Nova/$VARIANT_NAME$/Masked Mosaic"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Mask ("Mask", 2D) = "white" {}
8 _Size ("Size", Float) = 4.0
9 _Strength ("Mosaic Strength", Float) = 8.0
10 }
11 SubShader
12 {
13 $VARIANT_TAGS$
14 Pass
15 {
16 CGPROGRAM
17 #pragma vertex vert
18 #pragma fragment frag
19
20 #include "UnityCG.cginc"
21 #include "Assets/Nova/CGInc/Rand.cginc"
22
23 struct appdata
24 {
25 float4 vertex : POSITION;
26 float2 uv : TEXCOORD0;
27 float4 color : COLOR;
28 };
29
30 struct v2f
31 {
32 float4 vertex : SV_POSITION;
33 float2 uv : TEXCOORD0;
34 float4 color : COLOR;
35 };
36
37 v2f vert(appdata v)
38 {
39 v2f o;
40 o.vertex = UnityObjectToClipPos(v.vertex);
41 o.uv = v.uv;
42 o.color = v.color;
43 return o;
44 }
45
46 sampler2D _MainTex, _Mask;
47 float4 _MainTex_TexelSize;
48 float _T, _Size, _Strength;
49 $DEF_GSCALE$
50
51 fixed4 frag(v2f i) : SV_Target
52 {
53 float4 col = tex2D(_MainTex, i.uv);
54
55 // 计算当前像素在屏幕上的位置,并通过 floor 函数将其对齐到指定的马赛克块大小(_Size)
56 // 这使得每个马赛克块的中心都对齐
57 float4 screenPos = ComputeScreenPos(floor(i.vertex / _Size) * _Size);
58 // 使用自定义的 srand2 函数生成一个基于屏幕位置和时间的随机偏移量
59 // 这样每个像素的偏移是基于位置的,这能够给每个马赛克块引入轻微的随机化,使得效果更自然
60 float2 delta = srand2(float3(screenPos.xy, _Time.y));
61 // 根据随机偏移量 delta 对当前纹理坐标 uv 进行调整
62 // 调整后的 uv2 用来从主纹理中采样一个新的颜色,这个颜色将作为马赛克块的代表颜色
63 float2 uv2 = i.uv + delta * _MainTex_TexelSize.xy * $GSCALE$ * _Strength;
64 // 从调整后的纹理坐标 uv2 中采样颜色 col2
65 float4 col2 = tex2D(_MainTex, uv2);
66
67 // 从遮罩纹理 _Mask 中采样红色分量,值通常在 [0, 1] 范围内,用来控制马赛克效果的应用程度
68 // 将遮罩值乘以时间 _T,以便动态地控制遮罩效果的强度
69 float mask = tex2D(_Mask, i.uv).r * _T;
70 // 根据遮罩值 mask 在原始颜色 col 和采样到的 col2(马赛克块的颜色)之间进行插值
71 // mask 越大,col2 的影响就越强,表示遮罩区域内会应用更多的马赛克效果
72 col = lerp(col, col2, mask) * i.color;
73
74 $VARIANT_RGB$
75
76 return col;
77 }
78 ENDCG
79 }
80 }
81}
Mix Add
混合添加(Mix Add) 的效果,结合了主纹理、遮罩、颜色乘法、颜色加法等多种操作。通过使用这些操作,Shader 将主纹理和额外的效果进行混合,最后生成输出颜色。
- 主纹理采样:根据纹理坐标从主纹理中采样颜色,并结合顶点颜色。
- 遮罩控制:通过遮罩纹理控制哪些区域受到效果影响,反转遮罩的功能由
_InvertMask
控制。 - 颜色乘法和加法:通过
_ColorMul
进行颜色乘法(调整亮度或对比度),并通过_ColorAdd
对颜色进行偏移(加法)。 - 透明度调整:通过
_AlphaFactor
动态调整颜色的透明度。 - 动态变化:时间参数
_T
动态控制混合效果的强度。
1Shader "Nova/$VARIANT_NAME$/Mix Add"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Mask ("Mask", 2D) = "white" {}
8 _ColorMul ("Color Multiplier", Color) = (1, 1, 1, 1)
9 _ColorAdd ("Color Offset", Vector) = (0, 0, 0, 0)
10 _InvertMask ("Invert Mask", Float) = 0.0
11 _AlphaFactor ("Alpha Factor", Float) = 0.0
12 }
13 SubShader
14 {
15 $VARIANT_TAGS$
16 Pass
17 {
18 CGPROGRAM
19 #pragma vertex vert
20 #pragma fragment frag
21
22 #include "UnityCG.cginc"
23
24 $DEF_IADD_RGBA$
25
26 struct appdata
27 {
28 float4 vertex : POSITION;
29 float2 uv : TEXCOORD0;
30 float4 color : COLOR;
31 };
32
33 struct v2f
34 {
35 float4 vertex : SV_POSITION;
36 float2 uv : TEXCOORD0;
37 float2 uvMask : TEXCOORD1;
38 float4 color : COLOR;
39 };
40
41 sampler2D _Mask;
42 float4 _Mask_ST;
43
44 v2f vert(appdata v)
45 {
46 v2f o;
47 o.vertex = UnityObjectToClipPos(v.vertex);
48 o.uv = v.uv;
49 o.uvMask = TRANSFORM_TEX(v.uv, _Mask);
50 o.color = v.color;
51 return o;
52 }
53
54 sampler2D _MainTex;
55 float4 _ColorMul, _ColorAdd;
56 float _T, _InvertMask, _AlphaFactor;
57
58 fixed4 frag(v2f i) : SV_Target
59 {
60 // 从主纹理中根据纹理坐标 i.uv 采样颜色,乘以顶点颜色 i.color 得到最终颜色 col
61 float4 col = tex2D(_MainTex, i.uv) * i.color;
62 // 从遮罩纹理中采样红色通道的值,并存储在 mask 变量中
63 // 值通常在 [0, 1] 范围内,表示该像素是否应该受到影响
64 float mask = tex2D(_Mask, i.uvMask).r;
65 // 根据 _InvertMask 参数,决定是否反转遮罩效果
66 // 如果 _InvertMask 为 1,则遮罩会被反转(即黑色区域变为白色,白色区域变为黑色)
67 mask = _InvertMask + mask - 2 * _InvertMask * mask;
68
69 float4 maskColor;
70 // 根据 _InvertMask 参数,决定是否反转遮罩效果
71 // 如果 _InvertMask 为 1,则遮罩会被反转(即黑色区域变为白色,白色区域变为黑色)
72 maskColor.rgb = mask;
73 // 将主纹理的 alpha 通道值赋给 maskColor,确保透明度保持一致
74 maskColor.a = col.a;
75 // 应用颜色乘法因子 _ColorMul,这会调整颜色的亮度或对比度
76 maskColor *= _ColorMul;
77 // 通过自定义的宏 IADD_RGBA,将颜色偏移(_ColorAdd)添加到 maskColor 上
78 IADD_RGBA(maskColor, _ColorAdd)
79 // 通过 _AlphaFactor 参数调整 maskColor 的 alpha 通道值,从而影响透明度
80 maskColor.a *= _AlphaFactor;
81 // 将处理后的 maskColor 与原始颜色 col 混合。_T 参数控制混合的强度
82 col = saturate(col + _T * maskColor);
83
84 $VARIANT_RGB$
85
86 return col;
87 }
88 ENDCG
89 }
90 }
91}
Mono
Mono Shader 实现了一个简单的单色化(灰度化)效果,可以通过控制 _T 参数来调整原图和单色效果的过渡。具体而言,它会根据像素的亮度(灰度)生成一个灰度图像,然后通过混合原图和灰度图来创建一个逐渐转换为单色效果的过渡。
1Shader "Nova/$VARIANT_NAME$/Mono"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 // 颜色乘法因子,用来调整混合颜色的亮度或对比度
8 _ColorMul ("Color Multiplier", Color) = (1, 1, 1, 1)
9 // 颜色偏移(加法),将其添加到灰度图的颜色中,用于调整颜色的亮度、饱和度等
10 _ColorAdd ("Color Offset", Vector) = (0, 0, 0, 0)
11 }
12 SubShader
13 {
14 $VARIANT_TAGS$
15 Pass
16 {
17 CGPROGRAM
18 #pragma vertex vert
19 #pragma fragment frag
20
21 #include "UnityCG.cginc"
22
23 $DEF_IADD_RGBA$
24
25 struct appdata
26 {
27 float4 vertex : POSITION;
28 float2 uv : TEXCOORD0;
29 float4 color : COLOR;
30 };
31
32 struct v2f
33 {
34 float4 vertex : SV_POSITION;
35 float2 uv : TEXCOORD0;
36 float4 color : COLOR;
37 };
38
39 v2f vert(appdata v)
40 {
41 v2f o;
42 o.vertex = UnityObjectToClipPos(v.vertex);
43 o.uv = v.uv;
44 o.color = v.color;
45 return o;
46 }
47
48 sampler2D _MainTex;
49 float _T;
50 float4 _ColorMul, _ColorAdd;
51
52 fixed4 frag(v2f i) : SV_Target
53 {
54 float4 col = tex2D(_MainTex, i.uv) * i.color;
55 // 计算当前像素颜色的灰度值
56 // Luminance 是根据颜色的加权平均值来计算亮度
57 // 通常为 0.299 * R + 0.587 * G + 0.114 * B,人眼对各颜色的敏感度不同,因此加权不同
58 float gray = Luminance(col.rgb);
59
60 float4 mono;
61 // 将灰度值 gray 赋值给 mono.rgb,这样所有的 RGB 分量都等于灰度值,从而实现灰度图的效果
62 mono.rgb = gray;
63 // 保持原始颜色的 alpha 通道,确保透明度信息不变
64 mono.a = col.a;
65 // 将灰度图颜色应用颜色乘法因子 _ColorMul,可以用来调整灰度图的亮度或对比度
66 mono *= _ColorMul;
67 // 将颜色偏移 _ColorAdd 添加到灰度颜色 mono 上,从而调整最终的颜色
68 IADD_RGBA(mono, _ColorAdd)
69
70 // 根据时间参数 _T 混合原图和灰度图
71 //如果 _T 为 0,则完全是原图颜色, 如果 _T 为 1,则完全是灰度图
72 // lerp 是线性插值函数,计算两个值之间的加权平均
73 col = lerp(col, mono, _T);
74
75 $VARIANT_RGB$
76
77 return col;
78 }
79 ENDCG
80 }
81 }
82}
Motion Blur
运动模糊(Motion Blur)的效果,主要通过采样和加权周围的像素来模糊当前的像素,从而模拟物体或摄像机的运动轨迹。通过调节 _T
(时间),_Size
(模糊大小),_Theta
(方向角度),和 _Offset
(偏移量)来控制效果的强度和方向。
_MainTex
:主纹理,通常是场景的渲染图像,默认为白色纹理。
_T
:时间控制参数([0.0, 1.0]),用于控制运动模糊的强度。_T = 0
时没有模糊,_T = 1
时模糊最大。
_Size
:模糊的大小,决定了模糊的范围。值越大,模糊效果越强。
_Theta
:模糊的方向角度,决定了模糊的朝向。
_Offset
:模糊后的颜色偏移量,用来调整最终颜色的亮度。
1Shader "Nova/$VARIANT_NAME$/Motion Blur"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 1.0
8 _Theta ("Theta", Float) = 0.0
9 _Offset ("Offset", Float) = 0.0
10 }
11 SubShader
12 {
13 $VARIANT_TAGS$
14 Pass
15 {
16 CGPROGRAM
17 #pragma vertex vert
18 #pragma fragment frag
19
20 #include "UnityCG.cginc"
21 #include "Assets/Nova/CGInc/Blur.cginc"
22
23 struct appdata
24 {
25 float4 vertex : POSITION;
26 float2 uv : TEXCOORD0;
27 float4 color : COLOR;
28 };
29
30 struct v2f
31 {
32 float4 vertex : SV_POSITION;
33 float2 uv : TEXCOORD0;
34 float4 color : COLOR;
35 };
36
37 v2f vert(appdata v)
38 {
39 v2f o;
40 o.vertex = UnityObjectToClipPos(v.vertex);
41 o.uv = v.uv;
42 o.color = v.color;
43 return o;
44 }
45
46 sampler2D _MainTex;
47 float4 _MainTex_TexelSize;
48 float _T, _Size, _Theta, _Offset;
49 $DEF_GSCALE$
50
51 fixed4 frag(v2f i) : SV_Target
52 {
53 // 这个函数使用纹理采样和偏移来计算模糊的颜色
54 // _MainTex 是主纹理
55 // _MainTex_TexelSize * $GSCALE$ 是纹理的每个像素的大小
56 // i.uv 是当前纹理坐标
57 // float2(cos(_Theta), sin(_Theta)) 用于计算模糊方向
58 // _Size * _T 控制模糊的大小和强度
59 float4 col = tex2DMotionBlur(_MainTex,
60 _MainTex_TexelSize * $GSCALE$,
61 i.uv,
62 float2(cos(_Theta),
63 sin(_Theta)) * _Size * _T);
64 // 将纹理颜色与顶点颜色相乘,以实现顶点颜色对最终结果的影响
65 col *= i.color;
66 // 将 _Offset 加到颜色的 RGB 分量中,乘以 _T,这样可以根据时间调整偏移量
67 col.rgb += _Offset * _T;
68 // 将颜色的 RGB 分量限制在 [0, 1] 的范围内,防止颜色溢出
69 col.rgb = saturate(col.rgb);
70
71 $VARIANT_RGB$
72
73 return col;
74 }
75 ENDCG
76 }
77 }
78}
Overglow
Overglow 的效果,主要是通过根据时间 (_T)
动态调整纹理的缩放和平移,产生一种光晕效果。_Zoom
和 _Mul
控制效果的强度和缩放范围,_CenterX
和 _CenterY
确定光晕的中心位置。
_MainTex
:主纹理,用于渲染图像,默认为白色纹理。
_T
:时间控制参数,用于控制光晕效果的动态变化。_T = 0
时无变化,_T = 1
时光晕达到最大效果。
_Zoom
:光晕缩放值,决定了光晕的缩放范围。值越大,缩放越小,光晕越大。
_Mul
:乘法器,用于控制光晕效果的强度。
_CenterX, _CenterY
:确定光晕中心的位置。默认为纹理中心 (0.5, 0.5)
。
1Shader "Nova/$VARIANT_NAME$/Overglow"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Zoom ("Zoom", Float) = 0.5
8 _Mul ("Multiplier", Float) = 0.5
9 _CenterX ("Center X", Float) = 0.5
10 _CenterY ("Center Y", Float) = 0.5
11 }
12 SubShader
13 {
14 $VARIANT_TAGS$
15 Pass
16 {
17 CGPROGRAM
18 #pragma vertex vert
19 #pragma fragment frag
20
21 #include "UnityCG.cginc"
22
23 struct appdata
24 {
25 float4 vertex : POSITION;
26 float2 uv : TEXCOORD0;
27 float4 color : COLOR;
28 };
29
30 struct v2f
31 {
32 float4 vertex : SV_POSITION;
33 float2 uv : TEXCOORD0;
34 float4 color : COLOR;
35 };
36
37 v2f vert(appdata v)
38 {
39 v2f o;
40 o.vertex = UnityObjectToClipPos(v.vertex);
41 o.uv = v.uv;
42 o.color = v.color;
43 return o;
44 }
45
46 sampler2D _MainTex;
47 float _T, _Zoom, _Mul, _CenterX, _CenterY;
48
49 fixed4 frag(v2f i) : SV_Target
50 {
51 // 计算光晕效果的中心位置,使用了 _CenterX 和 _CenterY 来决定
52 float2 center = float2(_CenterX, _CenterY);
53 // 计算新的纹理坐标 uv2,它是基于原始纹理坐标 i.uv 按照中心位置 center 进行缩放和平移的
54 // 通过 (1.0 - _Zoom * _T) 来动态缩放纹理坐标
55 // _Zoom 控制缩放大小,_T 则是随着时间的推移变化,控制光晕的扩展效果
56 float2 uv2 = (i.uv - center) * (1.0 - _Zoom * _T) + center;
57 // 获取原始纹理坐标 i.uv 上的颜色值,并与顶点颜色相乘(i.color)
58 float4 col = tex2D(_MainTex, i.uv) * i.color;
59 // 获取基于新计算的纹理坐标 uv2 的颜色值,模拟偏移后的光晕效果
60 float4 col2 = tex2D(_MainTex, uv2) * i.color;
61 // 将光晕颜色 col2 加入到原始颜色 col 中
62 // _Mul 是强度控制,(1.0 - _T) 是控制光晕随时间的衰减
63 // 随着时间的变化,光晕效果的强度逐渐减弱
64 col.rgb += col2.rgb * _Mul * (1.0 - _T);
65 // 使用 saturate 函数将最终颜色的 RGB 分量限制在 [0, 1] 范围内,避免颜色溢出
66 col.rgb = saturate(col.rgb);
67
68 $VARIANT_RGB$
69
70 return col;
71 }
72 ENDCG
73 }
74 }
75}
Overlay
Overlay 效果,主要通过一个基本的顶点着色器和片元着色器来将一个纹理渲染到屏幕上,并且使用了 clamp 函数确保纹理坐标在 [0, 1] 范围内。它不做其他复杂的颜色混合或特效,只是将一个纹理应用到物体表面。
这种类型的 Shader 在图像叠加效果、UI 渲染或者背景图像渲染等场景中非常有用。例如,可以用它来叠加一个背景纹理,或者在游戏中特效图像层上叠加纹理,确保纹理总是渲染到屏幕上
1VARIANTS: Premul
2Shader "Nova/$VARIANT_NAME$/Overlay"
3{
4 Properties
5 {
6 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
7 }
8 SubShader
9 {
10 $VARIANT_TAGS$
11 ZTest Always
12
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20
21 #define clamped2D(tex, uv) tex2D((tex), clamp((uv), 0, 1))
22
23 struct appdata
24 {
25 float4 vertex : POSITION;
26 float2 uv : TEXCOORD0;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 };
34
35 v2f vert(appdata v)
36 {
37 v2f o;
38 // render directly to clip space
39 o.vertex = v.vertex;
40 o.uv = v.uv;
41 #ifdef UNITY_UV_STARTS_AT_TOP
42 o.uv.y = 1.0 - o.uv.y;
43 #endif
44 return o;
45 }
46
47 sampler2D _MainTex;
48
49 fixed4 frag(v2f i) : SV_Target
50 {
51 return clamped2D(_MainTex, i.uv);
52 }
53 ENDCG
54 }
55 }
56}
Radial Blur
Radial Blur(径向模糊)效果的 Shader,模糊是基于像素到中心的距离来进行计算的。下面我会详细讲解这段代码的每一部分,帮助你理解其实现原理。
_Size
:控制模糊的范围,越大越模糊。
_Offset
:控制模糊的偏移量。
_T
:时间控制,可能用来实现动画过渡的效果。
1Shader "Nova/$VARIANT_NAME$/Radial Blur"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 1.0
8 _Offset ("Offset", Float) = 0.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Blur.cginc"
21
22 struct appdata
23 {
24 float4 vertex : POSITION;
25 float2 uv : TEXCOORD0;
26 float4 color : COLOR;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 float4 color : COLOR;
34 };
35
36 v2f vert(appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41 o.color = v.color;
42 return o;
43 }
44
45 sampler2D _MainTex;
46 float4 _MainTex_TexelSize;
47 float _T, _Size, _Offset;
48 $DEF_GSCALE$
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 // 将纹理坐标从 [0, 1] 范围转换到以 (0.5, 0.5) 为中心的范围
53 // 这样做的目的是计算像素相对于中心点的偏移量
54 float2 uvShift = i.uv - 0.5;
55 // 实现模糊效果。根据 uvShift 的大小以及 _Size 和 _T 控制模糊的强度和范围
56 float4 col = tex2DMotionBlur(_MainTex,
57 _MainTex_TexelSize * $GSCALE$,
58 i.uv, uvShift * _Size * _T);
59 col *= i.color;
60 // 根据像素离中心点的距离(length(uvShift)),调整颜色的偏移量
61 // _Offset 控制了这个偏移的强度
62 col.rgb += _Offset * length(uvShift) * _T;
63 // 通过 saturate 函数将颜色限制在 [0, 1] 范围内,防止颜色溢出
64 col.rgb = saturate(col.rgb);
65
66 $VARIANT_RGB$
67
68 return col;
69 }
70 ENDCG
71 }
72 }
73}
Rain
一个模拟 雨滴效果 的视觉效果,基于时间和不同层次的降水模拟生成动画效果。
1Shader "Nova/$VARIANT_NAME$/Rain"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 2.0
8 _Aspect ("Aspect Ratio", Float) = 1.77777778
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Blur.cginc"
21 #include "Assets/Nova/CGInc/Rand.cginc"
22
23 struct appdata
24 {
25 float4 vertex : POSITION;
26 float2 uv : TEXCOORD0;
27 float4 color : COLOR;
28 };
29
30 struct v2f
31 {
32 float4 vertex : SV_POSITION;
33 float2 uv : TEXCOORD0;
34 float2 uvEffect : TEXCOORD1;
35 float4 color : COLOR;
36 };
37
38 float _Size, _Aspect;
39
40 v2f vert(appdata v)
41 {
42 v2f o;
43 o.vertex = UnityObjectToClipPos(v.vertex);
44 o.uv = v.uv;
45 o.uvEffect = float2(v.uv.x * _Size * _Aspect, v.uv.y * _Size);
46 o.color = v.color;
47 return o;
48 }
49
50 // 插值函数,让过渡更加自然
51 float S(float a, float b, float t)
52 {
53 t = saturate((t - a) / (b - a));
54 return t * t * (3.0 - 2.0 * t);
55 }
56
57 // 锯齿波函数
58 float saw(float b, float t)
59 {
60 return S(0.0, b, t) * S(1.0, b, t);
61 }
62
63 // 静态雨滴
64 float staticDrops(float2 uv, float t)
65 {
66 // 使用随机函数rand3生成随机雨滴位置
67 float3 n = rand3(floor(uv));
68 // 计算雨滴距离中心的长度d,决定雨滴大小
69 float d = length(frac(uv) - n.xy);
70 // 随机生成的z分量用于控制雨滴的出现时机(fade效果)
71 float fade = saw(0.5, frac(t + n.z));
72 // 输出雨滴亮度c,用于静态背景层的效果
73 float c = S(0.3, 0.0, d) * frac(n.z * 1000.0) * fade;
74 return c;
75 }
76
77 // 动态雨滴层
78 float2 dropLayer(float2 uv, float t)
79 {
80 float2 UV = uv;
81
82 // 表示雨滴的分布密度。横向有12列,纵向有2行
83 float2 grid = float2(12.0, 2.0);
84 // 为每列雨滴添加随机的偏移量,使其下落时间和速度不同,模拟自然随机性。
85 float colShift = rand(floor(uv.x * grid.x));
86 // 修改UV的 y 坐标,动态模拟雨滴的下落
87 uv.y += t + colShift;
88
89 // 将UV坐标映射到网格单元中
90 float2 uvGrid = uv * grid;
91 // 为当前网格单元生成随机值 n,其三个分量分别控制
92 // n.x:雨滴水平位置。
93 // n.y:雨滴拖尾的动态扰动。
94 // n.z:雨滴的生成时机。
95 float3 n = rand3(floor(uvGrid));
96 // 计算当前UV在网格单元内的局部坐标,并偏移到中心点
97 float2 st = frac(uvGrid) - float2(0.5, 0.0);
98
99 // 添加水平扰动和下落动画
100 // x 方向扰动:
101 // 初始的 x=n.x−0.5x=n.x−0.5,将雨滴位置中心化。
102 // 通过 wiggle 引入动态扰动,增加雨滴下落时的“左右摇摆”效果。
103 // x∗=0.7x∗=0.7:缩小水平移动范围,保持雨滴的自然性。
104 float x = n.x - 0.5;
105 float wiggle = UV.y * 20.0;
106 wiggle = sin(wiggle + sin(wiggle));
107 x += wiggle * (0.5 - abs(x)) * (n.y - 0.5);
108 x *= 0.7;
109 // 使用 saw 函数生成锯齿波模拟雨滴的下落位置,周期性出现新的雨滴
110 // frac(t + n.z) 保证每个雨滴的下落时机不同
111 float y = saw(0.8, frac(t + n.z));
112 // 计算当前点与雨滴中心 (x,y) 的距离 d
113 float d = length((st - float2(x, y)) * grid.yx);
114 // 使用平滑插值函数 S 渐变控制雨滴的形状,使雨滴看起来圆润且边缘模糊
115 float mainDrop = S(0.4, 0.0, d);
116
117 // 添加拖尾效果
118 // r:用于限制拖尾的起始位置,只在雨滴下方生成拖尾。
119 // trail:控制拖尾的宽度(通过 abs(st.x−x)abs(st.x−x) 计算),拖尾越靠近雨滴中心越强。
120 // trailFront:控制拖尾的长度,确保拖尾仅出现在雨滴下方且逐渐消失。
121 float r = S(1.0, y, st.y);
122 float trail = S(0.2, 0.0, abs(st.x - x));
123 float trailFront = S(-0.02, 0.02, st.y - y);
124 trail *= trailFront * r;
125
126 // 添加飞溅小雨滴
127 // 随机生成小雨滴,分布在主雨滴周围。
128 // 使用局部随机扰动(frac(UV.y * 10.0))生成多个小雨滴。
129 // 小雨滴的大小由距离 dddd 决定,靠近雨滴中心的区域更亮。
130 y = frac(UV.y * 10.0) + (st.y - 0.5);
131 float dd = length(st - float2(x, y));
132 float droplets = S(0.3, 0.0, dd);
133
134 // 合成主雨滴与其他效果
135 // 将主雨滴、拖尾和飞溅效果整合为一个整体亮度值 cc。
136 // 主雨滴 (mainDrop):主要亮度贡献。
137 // 拖尾 (trailFront):与主雨滴相连的拖尾区域。
138 // 飞溅雨滴 (droplets):细节效果,用于增加雨滴的动态感
139 float c = mainDrop + trailFront * droplets * r;
140
141 return float2(c, trail);
142 }
143
144 // 合并多个雨滴效果
145 // 该函数整合了 staticDrops 和 dropLayer 的效果,生成最终的雨滴效果
146 // 使用不同的参数(如 l0, l1, l2)来控制每个层次雨滴的强度
147 float2 drops(float2 uv, float t, float l0, float l1, float l2)
148 {
149 float s = staticDrops(uv * 40.0, t) * l0;
150 float2 m1 = dropLayer(uv * 0.5, t) * l1;
151 float2 m2 = dropLayer(uv, t) * l2;
152 float c = s + m1.x + m2.x;
153 c = S(0.0, 1.0, c);
154 float trail = m1.y + m2.y;
155 return float2(c, trail);
156 }
157
158 sampler2D _MainTex;
159 float4 _MainTex_TexelSize;
160 float _T;
161 $DEF_GSCALE$
162
163 fixed4 frag(v2f i) : SV_Target
164 {
165 float2 UV = i.uv;
166 float2 uv = i.uvEffect;
167 float t = _Time.y * 0.2;
168
169 float l0 = S(0.0, 1.0, _T) * 2.0;
170 float l1 = S(0.25, 0.75, _T);
171 float l2 = S(0.0, 0.5, _T);
172
173 float2 c = drops(uv, t, l0, l1, l2);
174 float2 e = float2(1e-3, 0.0);
175 float cx = drops(uv + e, t, l0, l1, l2).x;
176 float cy = drops(uv + e.yx, t, l0, l1, l2).x;
177 float2 n = float2(cx - c.x, cy - c.x);
178
179 float4 col = tex2D(_MainTex, UV + n);
180 float4 col2 = tex2DGaussianBlur(_MainTex,
181 _MainTex_TexelSize * $GSCALE$,
182 UV, 3.0 * _T) * (1.0 - 0.1 * _T);
183 col = lerp(col2, col, saturate(c.x + c.y)) * i.color;
184
185 $VARIANT_RGB$
186
187 return col;
188 }
189 ENDCG
190 }
191 }
192}
Rand Roll
实现了一个基于随机偏移的滚动效果,结合随机性为纹理添加动态移动或抖动效果。
1Shader "Nova/$VARIANT_NAME$/Rand Roll"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Freq ("Frequency", Float) = 10.0
8 }
9 SubShader
10 {
11 $VARIANT_TAGS$
12 Pass
13 {
14 CGPROGRAM
15 #pragma vertex vert
16 #pragma fragment frag
17
18 #include "UnityCG.cginc"
19 #include "Assets/Nova/CGInc/Rand.cginc"
20
21 struct appdata
22 {
23 float4 vertex : POSITION;
24 float2 uv : TEXCOORD0;
25 float4 color : COLOR;
26 };
27
28 struct v2f
29 {
30 float4 vertex : SV_POSITION;
31 float2 uv : TEXCOORD0;
32 float4 color : COLOR;
33 };
34
35 float _Freq;
36
37 v2f vert(appdata v)
38 {
39 v2f o;
40 o.vertex = UnityObjectToClipPos(v.vertex);
41 // 利用随机数函数 rand2 生成一个基于时间的随机偏移
42 o.uv = v.uv + rand2(floor(_Freq * _Time.y));
43 o.color = v.color;
44 return o;
45 }
46
47 sampler2D _MainTex;
48 float _T;
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 float4 col = tex2D(_MainTex, frac(i.uv)) * i.color;
53
54 $VARIANT_RGB$
55
56 return col;
57 }
58 ENDCG
59 }
60 }
61}
Ripple
Ripple Shader 实现了一个基于径向波纹的动态效果,能够在屏幕上模拟水波或其他类似的径向波动。画面中心扩散出波纹,随着时间传播,边缘区域的模糊增强,模拟动态水波或涟漪的视觉效果。
_MainTex
:主纹理,用于渲染波纹效果的基础图像。
_T
:控制波纹效果的时间参数,用于实现动态动画。
_Aspect
:屏幕的宽高比,确保波纹在不同分辨率下保持正确形状。
_Amp
:波纹的振幅,决定波纹的强度。
_RFreq
:波纹的径向频率,决定波纹的密集程度。
_TFreq
:波纹的时间频率,控制波纹随时间的传播速度。
_BlurSize
:模糊半径,用于增强波纹动态感。
1Shader "Nova/$VARIANT_NAME$/Ripple"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Aspect ("Aspect Ratio", Float) = 1.77777778
8 _Amp ("Amplitude", Float) = 0.01
9 _RFreq ("Radial Frequency", Float) = 50.0
10 _TFreq ("Time Frequency", Float) = 3.0
11 _BlurSize ("Blur Size", Float) = 1.0
12 }
13 SubShader
14 {
15 $VARIANT_TAGS$
16 Pass
17 {
18 CGPROGRAM
19 #pragma vertex vert
20 #pragma fragment frag
21
22 #include "UnityCG.cginc"
23 #include "Assets/Nova/CGInc/Blur.cginc"
24
25 struct appdata
26 {
27 float4 vertex : POSITION;
28 float2 uv : TEXCOORD0;
29 float4 color : COLOR;
30 };
31
32 struct v2f
33 {
34 float4 vertex : SV_POSITION;
35 float2 uv : TEXCOORD0;
36 float4 color : COLOR;
37 };
38
39 v2f vert(appdata v)
40 {
41 v2f o;
42 o.vertex = UnityObjectToClipPos(v.vertex);
43 o.uv = v.uv;
44 o.color = v.color;
45 return o;
46 }
47
48 sampler2D _MainTex;
49 float4 _MainTex_TexelSize;
50 float _T, _Aspect, _Amp, _RFreq, _TFreq, _BlurSize;
51 $DEF_GSCALE$
52
53 fixed4 frag(v2f i) : SV_Target
54 {
55 float2 uv = i.uv;
56 // 将纹理坐标中心移到 (0.5, 0.5)
57 uv -= 0.5;
58 // 考虑屏幕宽高比
59 uv.x *= _Aspect;
60 // 计算到中心的距离(半径)
61 float r = length(uv);
62 // 避免 r 为零
63 r = max(r, 1e-3);
64 // 计算波纹扰动
65 float dr = sin(_RFreq * r + _TFreq * _Time.y);
66 // 调整扰动强度,结合时间
67 dr = dr * dr * dr * _T;
68 // 修改 UV 坐标,应用波纹效果
69 uv *= (r + min(r, _Amp) * dr) / r;
70 // 恢复宽高比
71 uv.x /= _Aspect;
72 // 将坐标移回原点
73 uv += 0.5;
74
75 float4 col;
76 // 如果模糊大小大于零,使用高斯模糊
77 // 否则直接采样纹理
78 if (_BlurSize > 1e-3)
79 {
80 col = tex2DGaussianBlur(_MainTex,
81 _MainTex_TexelSize * $GSCALE$,
82 uv,
83 _BlurSize * abs(dr));
84 }
85 else
86 {
87 col = tex2D(_MainTex, uv);
88 }
89 col *= i.color;
90
91 $VARIANT_RGB$
92
93 return col;
94 }
95 ENDCG
96 }
97 }
98}
Ripple Move
Ripple 的增强版本,加入了波纹移动和时间渐变的功能,模拟波纹在特定方向上的动态传播,以及波纹的淡入和淡出效果。
_MainTex
:主纹理,用于渲染波纹效果的基础图像。
_T
:时间控制,范围为 [0.0, 1.0]
,用作波纹动画的核心参数。
_Aspect
:屏幕宽高比,确保波纹在非正方形画面中不失真。
_Amp
:波纹的振幅,决定扰动的强度。
_RFreq
:波纹的径向频率,决定波纹的密集程度。
_TFreq
:时间频率,控制波纹传播的速度。
_BlurSize
:模糊半径,用于增加动态视觉效果。
_Width
:波纹的峰值宽度,控制波纹的形状和集中程度。
_Fade
:淡入淡出的时间窗口,用于控制波纹的逐渐出现和消失。
方向性控制:引入额外的方向参数,使波纹可以沿某个方向传播,而非仅限于中心扩散。
多层波纹叠加:允许不同参数的波纹同时存在,模拟更复杂的水面效果。
颜色渐变:根据波纹扰动强度动态调整颜色,使波纹更加显著。
1Shader "Nova/$VARIANT_NAME$/Ripple Move"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Aspect ("Aspect Ratio", Float) = 1.77777778
8 _Amp ("Amplitude", Float) = 0.01
9 _RFreq ("Radial Frequency", Float) = 50.0
10 _TFreq ("Time Frequency", Float) = 3.0
11 _BlurSize ("Blur Size", Float) = 1.0
12 _Width ("Peak Width", Float) = 0.1
13 _Fade ("Fade Time", Float) = 0.1
14 }
15 SubShader
16 {
17 $VARIANT_TAGS$
18 Pass
19 {
20 CGPROGRAM
21 #pragma vertex vert
22 #pragma fragment frag
23
24 #include "UnityCG.cginc"
25 #include "Assets/Nova/CGInc/Blur.cginc"
26
27 struct appdata
28 {
29 float4 vertex : POSITION;
30 float2 uv : TEXCOORD0;
31 float4 color : COLOR;
32 };
33
34 struct v2f
35 {
36 float4 vertex : SV_POSITION;
37 float2 uv : TEXCOORD0;
38 // tex1.x:径向范围的最大值,用于波纹动画的归一化计算
39 // tex1.y:波纹的渐变因子,基于 _Fade 属性动态调整
40 float2 tex1 : TEXCOORD1; // x: r max, y: fade factor
41 float4 color : COLOR;
42 };
43
44 float _T, _Aspect, _Fade;
45
46 v2f vert(appdata v)
47 {
48 v2f o;
49 o.vertex = UnityObjectToClipPos(v.vertex);
50 o.uv = v.uv;
51 // 计算最大径向范围
52 o.tex1.x = 0.5 * sqrt(1.0 + _Aspect * _Aspect);
53 // 动态淡入淡出因子
54 o.tex1.y = saturate(_T / _Fade) * saturate((1.0 - _T) / _Fade);
55 o.color = v.color;
56 return o;
57 }
58
59 sampler2D _MainTex;
60 float4 _MainTex_TexelSize;
61 float _Amp, _RFreq, _TFreq, _BlurSize, _Width;
62 $DEF_GSCALE$
63
64 fixed4 frag(v2f i) : SV_Target
65 {
66 float2 uv = i.uv;
67 // 将中心点移到 (0.5, 0.5)
68 uv -= 0.5;
69 // 考虑屏幕宽高比
70 uv.x *= _Aspect;
71 // 计算到中心的径向距离
72 float r = length(uv);
73 // 避免半径为 0
74 r = max(r, 1e-3);
75
76 // 径向扰动,结合时间频率
77 float dr = sin(_RFreq * r + _TFreq * _Time.y);
78 // 归一化半径
79 float t = r / i.tex1.x;
80 // 计算扰动时间偏移
81 float dt = (_T - t) / _Width;
82 // 高斯分布控制波形宽度
83 dr /= 1.0 + dt * dt;
84 // 淡入淡出控制
85 dr *= i.tex1.y;
86
87 // 增强扰动强度的非线性效果
88 dr = dr * dr * dr;
89 // 应用扰动到 UV 坐标
90 uv *= (r + min(r, _Amp) * dr) / r;
91 // 恢复宽高比
92 uv.x /= _Aspect;
93 // 恢复 UV 坐标
94 uv += 0.5;
95
96 float4 col;
97 // 如果模糊大小大于零,使用高斯模糊
98 // 否则直接采样纹理
99 if (_BlurSize > 1e-3)
100 {
101 col = tex2DGaussianBlur(_MainTex,
102 _MainTex_TexelSize * $GSCALE$,
103 uv,
104 _BlurSize * abs(dr));
105 }
106 else
107 {
108 col = tex2D(_MainTex, uv);
109 }
110 col *= i.color;
111
112 $VARIANT_RGB$
113
114 return col;
115 }
116 ENDCG
117 }
118 }
119}
Roll
通过控制 UV 坐标的偏移,实现纹理的滚动效果。
1Shader "Nova/$VARIANT_NAME$/Roll"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _XSpeed ("X Speed", Float) = 0.0
8 _YSpeed ("Y Speed", Float) = 0.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20
21 struct appdata
22 {
23 float4 vertex : POSITION;
24 float2 uv : TEXCOORD0;
25 float4 color : COLOR;
26 };
27
28 struct v2f
29 {
30 float4 vertex : SV_POSITION;
31 float2 uv : TEXCOORD0;
32 float4 color : COLOR;
33 };
34
35 float _T, _XSpeed, _YSpeed;
36
37 v2f vert(appdata v)
38 {
39 v2f o;
40 o.vertex = UnityObjectToClipPos(v.vertex);
41 o.uv = v.uv + float2(_XSpeed, _YSpeed) * _Time.y * _T;
42 o.color = v.color;
43 return o;
44 }
45
46 sampler2D _MainTex;
47
48 fixed4 frag(v2f i) : SV_Target
49 {
50 float4 col = tex2D(_MainTex, frac(i.uv)) * i.color;
51
52 $VARIANT_RGB$
53
54 return col;
55 }
56 ENDCG
57 }
58 }
59}
Rotation Blur
_MainTex
:主纹理,应用模糊效果的基础图像。
_T
:时间因子,范围 [0.0, 1.0]
,控制模糊强度或动画进程。
_Size
:模糊半径,决定模糊范围大小。
_Offset
:颜色偏移值,用于在模糊效果中加入额外亮度或颜色调整。
1Shader "Nova/$VARIANT_NAME$/Rotation Blur"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 1.0
8 _Offset ("Offset", Float) = 0.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Blur.cginc"
21
22 struct appdata
23 {
24 float4 vertex : POSITION;
25 float2 uv : TEXCOORD0;
26 float4 color : COLOR;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 float4 color : COLOR;
34 };
35
36 v2f vert(appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41 o.color = v.color;
42 return o;
43 }
44
45 sampler2D _MainTex;
46 float4 _MainTex_TexelSize;
47 float _T, _Size, _Offset;
48 $DEF_GSCALE$
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 // 偏移 UV 以设置中心点为 (0.5, 0.5)
53 float2 uvShift = i.uv - 0.5;
54 float4 col = tex2DMotionBlur(_MainTex,
55 _MainTex_TexelSize * $GSCALE$,
56 i.uv,
57 float2(uvShift.y, -uvShift.x) * _Size * _T);
58 // 应用顶点颜色影响
59 col *= i.color;
60 // 根据偏移量调整颜色
61 col.rgb += _Offset * length(uvShift) * _T;
62 // 保持颜色值在 [0, 1] 范围内
63 col.rgb = saturate(col.rgb);
64
65 $VARIANT_RGB$
66
67 return col;
68 }
69 ENDCG
70 }
71 }
72}
Shake
实现一个简单的“抖动”效果,它会根据时间和频率在 X 和 Y 轴上对每个像素的 UV 坐标进行随机偏移,从而造成类似屏幕抖动的效果。
_MainTex
:主纹理,默认为白色纹理。
_T
:时间参数,范围为 [0.0, 1.0]
,用于控制时间的变化。这个值通常在运行时由 Unity 动态传递。
_XAmp
:X 轴方向的偏移幅度(振幅),控制水平方向的抖动强度。
_YAmp
:Y 轴方向的偏移幅度(振幅),控制垂直方向的抖动强度。
_Freq
:频率参数,控制抖动的频率(即抖动的速度)。
1Shader "Nova/$VARIANT_NAME$/Shake"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _XAmp ("X Amplitude", Float) = 1.0
8 _YAmp ("Y Amplitude", Float) = 1.0
9 _Freq ("Frequency", Float) = 10.0
10 }
11 SubShader
12 {
13 $VARIANT_TAGS$
14 Pass
15 {
16 CGPROGRAM
17 #pragma vertex vert
18 #pragma fragment frag
19
20 #include "UnityCG.cginc"
21 #include "Assets/Nova/CGInc/Rand.cginc"
22
23 struct appdata
24 {
25 float4 vertex : POSITION;
26 float2 uv : TEXCOORD0;
27 float4 color : COLOR;
28 };
29
30 struct v2f
31 {
32 float4 vertex : SV_POSITION;
33 float2 uv : TEXCOORD0;
34 float4 color : COLOR;
35 };
36
37 float _T, _XAmp, _YAmp, _Freq;
38
39 v2f vert(appdata v)
40 {
41 v2f o;
42 o.vertex = UnityObjectToClipPos(v.vertex);
43 o.uv = v.uv + float2(_XAmp, _YAmp) * rand2(floor(_Freq * _Time.y)) * _T;
44 o.color = v.color;
45 return o;
46 }
47
48 sampler2D _MainTex;
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 float4 col = tex2D(_MainTex, i.uv) * i.color;
53
54 $VARIANT_RGB$
55
56 return col;
57 }
58 ENDCG
59 }
60 }
61}
Sharpen
实现了一个简单的 锐化 (Sharpen) 效果。锐化效果通过增加图像的边缘对比度来使图像变得更加清晰。具体来说,这个着色器通过从纹理中采样像素,并将这些像素与其模糊版本进行比较,从而增强图像的细节部分。
在片元着色器中,首先从主纹理中采样原始颜色,然后将原始图像和模糊图像之间的差值(边缘信息)加回去,从而增强图像的细节。
1Shader "Nova/$VARIANT_NAME$/Sharpen"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 1.0
8 _Strength ("Strength", Float) = 1.0
9 }
10 SubShader
11 {
12 $VARIANT_TAGS$
13 Pass
14 {
15 CGPROGRAM
16 #pragma vertex vert
17 #pragma fragment frag
18
19 #include "UnityCG.cginc"
20 #include "Assets/Nova/CGInc/Blur.cginc"
21
22 struct appdata
23 {
24 float4 vertex : POSITION;
25 float2 uv : TEXCOORD0;
26 float4 color : COLOR;
27 };
28
29 struct v2f
30 {
31 float4 vertex : SV_POSITION;
32 float2 uv : TEXCOORD0;
33 float4 color : COLOR;
34 };
35
36 v2f vert(appdata v)
37 {
38 v2f o;
39 o.vertex = UnityObjectToClipPos(v.vertex);
40 o.uv = v.uv;
41 o.color = v.color;
42 return o;
43 }
44
45 sampler2D _MainTex;
46 float4 _MainTex_TexelSize;
47 float _T, _Size, _Strength;
48 $DEF_GSCALE$
49
50 fixed4 frag(v2f i) : SV_Target
51 {
52 float4 col = tex2D(_MainTex, i.uv);
53 col += _Strength * _T * (col - tex2DGaussianBlur(_MainTex, _MainTex_TexelSize * $GSCALE$, i.uv, _Size * _T));
54 col *= i.color;
55
56 $VARIANT_RGB$
57
58 return col;
59 }
60 ENDCG
61 }
62 }
63}
Water
这个着色器创建了一种模拟水面波动和失真效果的视觉效果。
_MainTex
:主纹理。
_T
:时间变量,用于驱动波动效果。
_Size
:波纹的缩放比例。
_Aspect
:波纹的宽高比,用于调整波动效果适应屏幕比例。
_Freq
:波动的频率,决定波动的细节。
_Distort
:失真强度,控制纹理变形的幅度。
1Shader "Nova/$VARIANT_NAME$/Water"
2{
3 Properties
4 {
5 [HideInInspector] _MainTex ("Main Texture", 2D) = "white" {}
6 _T ("Time", Range(0.0, 1.0)) = 0.0
7 _Size ("Size", Float) = 10.0
8 _Aspect ("Aspect Ratio", Float) = 1.77777778
9 _Freq ("Frequency", Float) = 50.0
10 _Distort ("Distort", Float) = 5.0
11 }
12 SubShader
13 {
14 $VARIANT_TAGS$
15 Pass
16 {
17 CGPROGRAM
18 #pragma vertex vert
19 #pragma fragment frag
20
21 #include "UnityCG.cginc"
22 #include "Assets/Nova/CGInc/Rand.cginc"
23
24 #define PI 3.1415927
25
26 struct appdata
27 {
28 float4 vertex : POSITION;
29 float2 uv : TEXCOORD0;
30 float4 color : COLOR;
31 };
32
33 struct v2f
34 {
35 float4 vertex : SV_POSITION;
36 float2 uv : TEXCOORD0;
37 float2 uvEffect : TEXCOORD1;
38 float4 color : COLOR;
39 };
40
41 float _Size, _Aspect;
42
43 v2f vert(appdata v)
44 {
45 v2f o;
46 o.vertex = UnityObjectToClipPos(v.vertex);
47 o.uv = v.uv;
48 o.uvEffect = float2(v.uv.x * _Size * _Aspect, v.uv.y * _Size);
49 o.color = v.color;
50 return o;
51 }
52
53 float _T, _Freq;
54
55 float water(float2 uv)
56 {
57 // 定义波动层数
58 const float n = 3.1;
59 float h = 0.0;
60 // 累加多个频率和振幅的波动
61 for (float i = 1.0; i <= n; i += 0.7)
62 {
63 // 按层调整纹理坐标频率
64 float2 p = uv * i * i;
65 // 添加随时间变化的相位移动
66 p.y += _Freq / i * _Time.y;
67 // cos(2.0 * PI * noise(p) * _T):利用噪声函数生成波动的振幅
68 // (i * i * i):递减波动振幅,模拟更高频率波动的影响逐渐减弱
69 h += cos(2.0 * PI * noise(p) * _T) / (i * i * i);
70 }
71 // h 为该点的波动高度
72 return h;
73 }
74
75 float2 waterNormal(float2 uv)
76 {
77 // 用于计算法线的小偏移
78 float2 e = float2(1e-3, 0.0);
79 // 当前点波动高度
80 float h0 = water(uv);
81 // 相邻点波动高度
82 float h1 = water(uv + e);
83 float h2 = water(uv + e.yx);
84 // 计算高度差,形成一个二维向量,表示该点的水面法线
85 return float2(h1 - h0, h2 - h0);
86 }
87
88 sampler2D _MainTex;
89 float4 _MainTex_TexelSize;
90 float _Distort;
91
92 fixed4 frag(v2f i) : SV_Target
93 {
94 // 计算法线的二维偏移量,_Distort 决定偏移强度
95 float2 uvDelta = _Distort * waterNormal(i.uvEffect);
96 // 利用偏移后的 UV 坐标从主纹理采样,并应用顶点颜色
97 float4 col = tex2D(_MainTex, i.uv + uvDelta) * i.color;
98
99 $VARIANT_RGB$
100
101 return col;
102 }
103 ENDCG
104 }
105 }
106}
Burn
实现烧毁的特效,简单的说就是需要
1Shader "Custom/BurnWithCompleteBlack"
2{
3 Properties
4 {
5 _MainTex ("Main Texture", 2D) = "white" {} // 主纹理
6 _BurnMask ("Burn Mask", 2D) = "white" {} // 烧毁遮罩
7 _BurnThreshold ("Burn Threshold", Range(0, 1)) = 0.0 // 烧毁阈值
8 _BurnEdgeWidth ("Burn Edge Width", Range(0.01, 0.2)) = 0.05 // 烧毁边缘宽度
9 _BurnEdgeColor ("Burn Edge Color", Color) = (1, 0.3, 0, 1) // 火焰边缘颜色
10 _BurnCenterColor ("Burn Center Color", Color) = (0, 0, 0, 1) // 烧毁中心颜色
11 }
12 SubShader
13 {
14 Tags { "Queue"="Transparent" "RenderType"="Transparent" }
15 LOD 200
16
17 Pass
18 {
19 Blend SrcAlpha OneMinusSrcAlpha
20 ZWrite Off
21 Cull Off
22
23 CGPROGRAM
24 #pragma vertex vert
25 #pragma fragment frag
26
27 #include "UnityCG.cginc"
28
29 struct appdata_t
30 {
31 float4 vertex : POSITION;
32 float2 uv : TEXCOORD0;
33 };
34
35 struct v2f
36 {
37 float4 pos : SV_POSITION;
38 float2 uv : TEXCOORD0;
39 };
40
41 sampler2D _MainTex;
42 sampler2D _BurnMask;
43 float _BurnThreshold;
44 float _BurnEdgeWidth;
45 float4 _BurnEdgeColor;
46 float4 _BurnCenterColor;
47
48 v2f vert (appdata_t v)
49 {
50 v2f o;
51 o.pos = UnityObjectToClipPos(v.vertex);
52 o.uv = v.uv;
53 return o;
54 }
55
56 fixed4 frag (v2f i) : SV_Target
57 {
58 // 获取主纹理颜色
59 fixed4 mainColor = tex2D(_MainTex, i.uv);
60
61 // 获取烧毁遮罩值
62 float burnValue = tex2D(_BurnMask, i.uv).r;
63
64 // 计算边缘范围
65 float edgeStart = _BurnThreshold - _BurnEdgeWidth;
66 float edgeEnd = _BurnThreshold;
67
68 // 强制判断是否完全烧毁
69 if (_BurnThreshold >= 1.0 || burnValue < edgeStart)
70 {
71 // 完全烧毁,设置为中心颜色
72 return _BurnCenterColor;
73 }
74 else if (burnValue < edgeEnd)
75 {
76 // 边缘范围内的颜色过渡
77 float edgeFactor = (burnValue - edgeStart) / (_BurnEdgeWidth);
78 return lerp(_BurnEdgeColor, _BurnCenterColor, edgeFactor);
79 }
80 else
81 {
82 // 未烧毁区域显示主纹理
83 return mainColor;
84 }
85 }
86 ENDCG
87 }
88 }
89}