水滴流动特效(一)

前言

水滴流动特效是开放世界游戏非常常见的效果,在shadertoy看到一个没有使用任何贴图做的效果:
https://www.shadertoy.com/view/ltffzl

这个作者在Youtube上分享了他制作这个效果的过程
https://www.youtube.com/watch?v=EBrAdahFtuo

这里就将他制作的过程整理成图文教程

水滴圆点的制作

1
2
3
4
5
6
7
8
float4 col = 0;
float2 uv = inputUV * _Size; //_Size控制tilling
float2 gv = frac(uv)-.5;
float2 dropPos = gv;
float drop = smoothstep(.05, .03, length(dropPos));
if(gv.x > .48 || gv.y > .49) col = float4(1,0,0,1); //绘制红线
col += drop;
retun col;

由于正方形的话水滴间距离有点太大,所以增加aspect参数,把每个区域变成长方形

1
2
3
4
5
6
float2 aspect = float2(2, 1);
float2 uv = i.uv * _Size * aspect; //_Size控制tilling
float2 gv = frac(uv)-.5;
float2 dropPos = gv / aspect;
float drop = smoothstep(.05, .03, length(dropPos));
if(gv.x > .48 || gv.y > .49) col = float4(1,0,0,1); //绘制红线

水滴的移动

1
2
t = _Time.y;
uv.y += t * .25;

接着希望给水滴小圆点的移动做一些offset,使得它下落先慢后快

1
2
3
4
float x = 0;
float y = -sin(t+sin(t+sin(t)*.5)) * .45; //不乘0.45水滴会是半个
float2 dropPos = (gv - float2(x,y))/aspect;
float drop = smoothstep(.05, .03, length(dropPos));

水滴的轨迹

小水珠

因为水滴在物体表面下落时会在轨迹上留下小水珠,所以要在水滴后面加上小水珠,这些小水珠位置是不动的,所以要加一个向后倒退的速度来抵消现在的0.25t的速度

1
2
3
4
5
6
7
8
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)-.5)/8;
float trail = S(.03, .01, length(trailPos));
col += trail;
col += drop;

拖尾

除了小水珠之外,还有比较薄的一层水质,这个区域可以来作为许多东西的判定,比如水滴划过的地方在玻璃等表面会刷掉气雾小水珠,在原来干燥的表面划过的地方会有潮湿的质感

1
2
3
// 先在水滴后部加上
float fogTrail = S(-.05, .05, dropPos.y);
col += fogTrail * .5;

1
2
3
4
// 加上逐渐消失的效果
float fogTrail = S(-.05, .05, dropPos.y);
fogTrail *= S(.5, y, gv.y);
col += fogTrail * .5;

1
2
3
4
5
6
7
8
9
// 用这个fogtrail的结果乘到之前的trail上,使小水滴也逐渐消失,并把宽度限定在水滴的宽度
float fogTrail = S(-.05, .05, dropPos.y);
fogTrail *= S(.5, y, gv.y);
trail *= fogTrail;
fogTrail *= S(.05, .04, abs(dropPos.x));
col += fogTrail * .5;
col += fogTrail * .5;
col += trail;
col += drop;

水滴的横向扭动

1
2
3
这个就选一个自己认为合适的函数给x就行,这里使用:
float w = i.uv.y * 10;
float x = sin(3*w)*pow(sin(w), 6)*.45;

增加随机性

显然这些水滴不能这么整齐,这里使用一个随机函数,可以把一个float2随机到(0, 1)的float

1
2
3
4
5
float N21(float2 p){
p = frac(p*float2(123.34, 345.45));
p += dot(p, p+34.345);
return frac(p.x*p.y);
}

输入uv就是这样的结果

1
2
3
4
// 给每个长方形区域一个随机的值
float2 id = floor(uv);
float n = N21(id);
return n;

return n就是这个结果,每个长方形区域有一个随机的(0, 1)的值

利用这个值就可以让每个雨滴有一些自己的变化

1
2
3
4
5
//给之前x轴上的变化加一些随机性
float w = inputUV.y * 10;
//float x = sin(3*w)*pow(sin(w), 6)*.45;
float x = (n - .5)*.8;
x += (.4-abs(x)) * sin(3*w)*pow(sin(w), 6)*.45;

1
2
//使不同雨滴有时差
t += n*6.2831;

使用水滴

水滴在物体表面上会有折射的效果,我们直接对贴图采样时的uv值做偏移也能达到类似的效果

1
2
float2 offs = drop * dropPos + trail * trailPos;
col = tex2D(_MainTex, i.uv + offs * _Distortion);

输出offset*10:

输出col: