Shader练习1

关于一些unity shader的学习内容。

Posted by 秦浩凯(Haokai Qin) on 2023-01-28
Estimated Reading Time 8 Minutes
Words 1.7k In Total
Viewed Times

基础定义

https://blog.csdn.net/seek_yang/article/details/106262779

基础光照模型的实现

https://blog.csdn.net/Liuees/article/details/120208166

雨水

涟漪

https://zhuanlan.zhihu.com/p/83219238
https://zhuanlan.zhihu.com/p/182459720

雨痕玻璃:水雾模糊 + 雨滴轨迹

[推荐] https://www.youtube.com/watch?v=EBrAdahFtuo
https://blog.csdn.net/weixin_42872938/article/details/104156154

origin
origin

雨滴轨迹的制作

首先创建基本的雨滴轨迹。

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

v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.grabUv = UNITY_PROJ_COORD(ComputeGrabScreenPos(o.vertex));
UNITY_TRANSFER_FOG(o,o.vertex);


return o;
}


fixed4 frag(v2f i) : SV_Target
{
float4 col = 0;

// 用size将整个物体划分为边长为size(网格数)的网格
float2 uv = i.uv * _Size;

// 获取网格内坐标。 -.5 偏移, 以令雨滴生成在网格中部
float2 gv = frac(uv) - .5;


float t = _Time.y * 0 + _T;

// 偏移,但用处不大。
uv.y += t * .25;


// 计算雨滴位置
// 为便于观察,暂不增加X轴偏移
float x = 0;
// 让y轴下落加快
float y = -sin(t + sin(t + sin(t) * .5)) * .45;

// 雨滴位置 = 当前在网格的位置 + 偏移
float2 dropPos = (gv - float2(x, y)) ;

// 用平滑得到雨滴处的颜色。
float drop = S(.05, .03, length(dropPos));
col += drop;

// 拖尾轨迹位置(模拟不同时刻的拖尾位置)
float2 trailPos = (gv - float2(x, t* .25));

// 计算拖尾在Y轴上的偏移量。
// *4 是生成的雨滴数量。
trailPos.y = (frac(trailPos.y * 4) -.5) / 8;
// 同样地,用平滑生成拖尾的颜色。
float trail = S(.03, .01, length(trailPos));

trail *= S(-.05, .05, dropPos.y);
trail *= S(.5, y, gv.y);
col += trail;
col += drop;

// 绘制网格边界。
if (gv.x > .48 || gv.y > .49) col = float4(1,0,0,1);

return col;


origin

接下来,让雨滴的方向产生变化。

1
2
3
4
5

float w = i.uv.y * 10;
float x = sin(3*w)*pow(sin(w), 6)* .45;
float y = -sin(t + sin(t + sin(t) * .5)) * .45;
y -= (gv.x - x)*(gv.x - x);

origin

随后,添加一段拖尾效果。

1
2
3
4
5
6
7
8
9
10
11

float frogTrail = S(-.05, .05, dropPos.y);
frogTrail *= S(.5, y, gv.y);
trail *= frogTrail;
frogTrail *= S(.05, .04, abs(dropPos.x));


// trail *= S(-.05, .05, dropPos.y);
// trail *= S(.5, y, gv.y);

col += frogTrail * .5;

接下来,为雨滴拖尾增加一个范围(雨滴轨迹上没有雾气的区域)。

1
2
3
4
5
6
7
8
9

//离该物体越远,fade越接近0,越近,fade越接近1
float fade = 1 - saturate(fwidth(i.uv) * 60);

//模糊的等级,实现水滴的拖尾没有雾气效果
float blur = _Blur * 7 * (1 - drops.z * fade);

float2 projUv = i.grabUv.xy / i.grabUv.w;
projUv += drops.xy * _Distortion * fade;

origin

接下来,为雨滴增加折射(偏移采样其他位置的纹理)。

1
2
3
4
5
6
7

//在有水滴的地方给采样坐标做一个偏移,以表现光的折射
float2 offs = drop * dropPos + trail * trailPos;

// 采样后方的纹理
col= tex2D(_MainTex,i.uv+offs*_Distortion);
return float3(offs, frogTrail);

水雾模糊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

//使用每一像素取和周围像素的平均值来实现模糊,来模拟雾气效果
blur *= 0.01;
//迭代的次数
const float numSamples = 16;
//搜索附近像素的开始位置吗,在0到2Π之间

float a = N21(i.uv) * 6.18;
for (float i = 0; i < numSamples; i++) {
float2 offs = float2(sin(a),cos(a)) * blur;
float d = frac(sin((i + 1) * 546.) * 5425.);
d = sqrt(d);
offs *= d;
col += tex2D(_GrabTexture,projUv + offs);
a++;
}

完整代码

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
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

Shader "Custom/RainedGlassEffects"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Size("Size",float) = 1
_t("Time",float) = 1
_Distortion("Distortion",Range(-5,5)) = 1
_Blur("blur",range(0,1)) = 0
}

SubShader
{
Tags { "RenderType" = "Opaque" "Queue" = "Transparent"}
LOD 100
GrabPass{"_GrabTexture"}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#define S(a,b,c) smoothstep(a,b,c) //使用S代替该方法,比较方便
#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;

// float4 TEXCOORD : TEXTCOORD;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 grabUv : TEXCOORD1;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;


//// ADD From
//float4 uv23 : TEXCOORD2;
//float4 uv45 : TEXCOORD3;
};

sampler2D _MainTex,_GrabTexture;
float4 _MainTex_ST;
float _Size,_t, _T,_Distortion,_Blur;

float4 offsets;

v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.grabUv = UNITY_PROJ_COORD(ComputeGrabScreenPos(o.vertex));
UNITY_TRANSFER_FOG(o,o.vertex);


return o;
}

//使用一个二维向量生成一个伪随机数
float N21(float2 p)
{
p = frac(p * float2(123.34,345.45));
p += dot(p,p + 34.345);//返回两个向量的点击
return frac(p.x * p.y);
}


float3 Layer(float2 UV, float t)
{
// float t = fmod(_Time.y + _T, 7200);

float4 col = 0;

float2 aspect = float2(2, 1);
float2 uv = UV * _Size * aspect;

uv.y += t * 0.25;

float2 gv = frac(uv) - .5;
float2 id = floor(uv);

float n = N21(id);
t += n*6.18;

float w = UV.y * 10;

float x = (n - .5) * .8; // sin(3 * w)* pow(sin(w), 6) * .45;
x += (.4 - abs(x)) * sin(3 * w) * pow(sin(w), 6) * .45;

float y = -sin(t + sin(t + sin(t) * .5)) * .45;
y -= (gv.x - x) * (gv.x - x);

float2 dropPos = (gv - float2(x, y)) / aspect;
float drop = S(.05, .03, length(dropPos));

float2 trailPos = (gv - float2(x, t * .25)) / aspect;
trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8;

float trail = S(.03, .01, length(trailPos));
float frogTrail = S(-.05, .05, dropPos.y);
frogTrail *= S(.5, y, gv.y);
trail *= frogTrail;
frogTrail *= S(.05, .04, abs(dropPos.x));


// trail *= S(-.05, .05, dropPos.y);
// trail *= S(.5, y, gv.y);

col += frogTrail * .5;
col += trail;
col += drop;

if (gv.x > .48 || gv.y > .49)
col = float4(1, 0, 0, 1);

//col *= 0;
//col += N21(i.uv);
// col.rg = id* .1;

// float2 offs = drop * dropPos + trail * trailPos;

//在有水滴的地方给采样坐标做一个偏移,以表现光的折射
float2 offs = drop * dropPos + trail * trailPos;
//col= tex2D(_MainTex,i.uv+offs*_Distortion);
return float3(offs, frogTrail);

}



fixed4 frag(v2f i) : SV_Target
{
//// test
//float4 col = 0;
//float2 uv = i.uv * _Size;


//float2 gv = frac(uv) -.5;


//float t = _Time.y * 0 + _T;
//uv.y += t * .25;
//float x = 0;
//float y = -sin(t + sin(t + sin(t) * .5)) * .45;

//float2 dropPos = (gv - float2(x, y)) ;
//float drop = S(.05, .03, length(dropPos));
//col += drop;

//float2 trailPos = (gv - float2(x, t* .25));
//trailPos.y = (frac(trailPos.y * 4) -.5) / 8;
//float trail = S(.03, .01, length(trailPos));

//col += trail;


//if (gv.x > .48 || gv.y > .49) col = float4(1,0,0,1);
//return col;
//// test



//t表示时间,没两小时重置一次,以防时间数字太大导致伪随机数露馅
float t = fmod(_Time.y + _t,7200);
float4 col = 0;
float3 drops = Layer(i.uv,t);
drops += Layer(i.uv * 1.11 + 8.43,t);
//fwidth为偏导函数,表示屏幕上该像素与周边像素的差别,越近越小,越远越大
//saturate为将变量控制在0到1之间,小于0返回0,大于1返回1
//离该物体越远,fade越接近0,越近,fade越接近1
float fade = 1 - saturate(fwidth(i.uv) * 60);

//模糊的等级,实现水滴的拖尾没有雾气效果
float blur = _Blur * 7 * (1 - drops.z * fade);

//col = tex2Dlod(_MainTex,float4(i.uv+drops.xy*_Distortion,_textNum,blur));

float2 projUv = i.grabUv.xy / i.grabUv.w;
projUv += drops.xy * _Distortion * fade;
//使用每一像素取和周围像素的平均值来实现模糊,来模拟雾气效果
blur *= 0.01;
//迭代的次数
const float numSamples = 16;
//搜索附近像素的开始位置吗,在0到2Π之间

float a = N21(i.uv) * 6.18;
for (float i = 0; i < numSamples; i++) {
float2 offs = float2(sin(a),cos(a)) * blur;
float d = frac(sin((i + 1) * 546.) * 5425.);
d = sqrt(d);
offs *= d;
col += tex2D(_GrabTexture,projUv + offs);
a++;
}
col /= numSamples;
return col * 0.9;
}
ENDCG
}
}
}


题外话,关于bloom

模糊效果当然也可以在后效阶段处理。

https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/

较复杂的玻璃计算

https://blog.csdn.net/qq_32468649/article/details/79992819

进阶:BRDF shader


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !