水滴分为 静态水滴 + 动态水滴 + 往下落的不连续水珠(圆点水珠+拖尾)
1.首先我们期望在长宽比为1:1的很多正方形块上进行绘制,矫正,窗口大小怎么变化不影响显示

2.我们定义完水滴的框架: 静态水滴 + 动态水滴 + 滴落过程函数
float staticDrops(float2 uv)
{
uv *= 40;//想让屏幕中有多少个条纹就*=多少条纹
float2 id = floor(uv);
uv = frac(uv) - 0.5;//-0.5是让亮度变暗
return uv.x;
}
float dynamicDrops()
{
return float2(0, 0);
}
float2 drops(float2 uv)
{
float sd = staticDrops(uv);
float2 dd = dynamicDrops();
return float2(sd, dd) + dd;
}
fixed4 frag (v2f i) : SV_Target
{
float2 uv = i.uv;
uv.x *= _ScreenParams.x / _ScreenParams.y;
float2 c = drops(uv);
return c.x;
}
ENDCG
3.我们需要确定水滴的圆: 需要用极坐标系用两个值来描述一个点的位置,然后画出圆
极坐标定义: UV 转极坐标的公式: (uv.x, uv.y) r = length(uv);
一般情况下,我们会得到这种图形.

但是在项目中,我们不能保留全屏水珠(圆),需要随机的水珠(圆),那就需要加入随机值(引入噪声模式)
由此,我们需要用uv 减去随机数,得到r = length(uv - p);
4.在第二步的框架上,我们需要计算噪声网格r = length(uv - p),求出p,分为以下5步.
4.1 打乱原始uv
4.2 将uv扩展成三维向量
4.3 由于还不够乱,所以我们将 dot(pt3,pt3.yzx) 的结果加到 pt3 的每个分量上
4.4 提取每一个分量的小数部分,否则过亮导致曝光
4.5 输出噪声网格的值
5.我们需要得到圆点的灰度值, 和随机的整个圆(不能让圆点被过于裁切),这里需要微调

//...上面保持不变
float staticDrops(float2 uv)
{
uv *= 10;
float2 id = floor(uv);
float3 n3 = noiseGrid(id);
uv = frac(uv) - 0.5;
float2 p = (n3.xy - 0.5) * 0.7;
float radius = length(uv - p);
float c = smoothstep(0.3, 0, radius);
return c;
}
//下面保持不变
6.我们要实现白色点有深有浅,然后随机变化
6.1 白色点有深有浅,我们需要提取随机值的z象限,乘上小数部分. 小数 * 小数,返回值都是0 - 1.
6.2 随机变化需要引入时间t,增加fade引入,形成类似马赛克一直在转动

6.3 我们混合之前的图片: 将return fade 换成 return c

7.计算水珠流动
7.1 水珠需要从上往下流下来, uv需要偏移: uv.y += t;
7.2 引入一维随机值小数
7.3 引入scale 和 scale2 : 为了拉伸之前的正方形网格从正方形块变成长条
//...noiseGrid不变
float noise(float n)
{
return frac(sin(n * 12345.678) * 87654.321);
}
//staticDrops不变
float dynamicDrops(float2 uv, float t)
{
uv.y += t; //让uv.y从上往下变化
float2 scale = float2(6, 1);//让uv从网格变成竖条
float2 scale2 = scale * 2; // 两个scale让流动更圆滑
float2 id = floor(uv * scale2);
uv.y += noise(id.x); //让uv.y随机变化
float2 st = frac(uv * scale2);
return st;
return float2(0, 0);
}
float2 drops(float2 uv, float t)
{
float sd = staticDrops(uv, t);
float2 dd = dynamicDrops();
return float2(sd, dd) + dd;
}
fixed4 frag (v2f i) : SV_Target
{
//...其余不变
float t = _Time.x;
return c.x;
}
ENDCG
由此实现下面竖条纹向下流动的图片动画
8.难点: 叠加水滴: 还是只修改dynamicDrops
再次用UV 转极坐标 length
8.1 首先然后需要求圆,需要减去float(向量)去确定中心,确保不被太过裁切
8.2 然后需要缩放一下数值,不是上图中所有需要灰度渐变
8.3 再次构建一个二维向量float2 p, 用length((st - p) * scale.yx)确保下落的条状变成圆
//...noiseGrid不变
//...noise不变
float noise(float n)
{
return frac(sin(n * 12345.678) * 87654.321);
}
//staticDrops不变
float dynamicDrops(float2 uv, float t)
{
uv.y += t; //让uv.y从上往下变化
float2 scale = float2(6, 1);//让uv从网格变成竖条
float2 scale2 = scale * 2; // 两个scale让流动更圆滑
float2 id = floor(uv * scale2);
uv.y += noise(id.x); //让uv.y随机变化
float3 n3 = noiseGrid(id);
float2 st = frac(uv * scale2) - float(0, 0.5);//确定中心
float x = n3.x - 0.5;
float y = smoothstep(0.8, 0, frac(t + n3.z));
float2 p = float2(x, y);
float d = length((st - p) * scale.yx);
return d;
}
//Drops变成return dd;
fixed4 frag (v2f i) : SV_Target
{
//...其余不变
float t = _Time.x * _Speed;
return c.x;
}
ENDCG

9.加入pingPang(v, t)函数, 不让雨滴均匀下落, 增加一个插值效果.
float pingPong(float v, float t)
{
return smoothstep(0, v, t) * smoothstep(1, v, t);
}
float dynamicDrops(float2 uv, float t)
{
//..其余保持不变
float y = smoothstep(0.85, 0, frac(t + n3.z);
y = pingPang(0.85, frac(t + n3.z));
//..其余保持不变
}

- 绘制水滴,将求到的圆限制在0.4 - 0之间
需要向x方向添加扭曲效果,sin() 括号中的值为随机写入
float pingPong(float v, float t)
{
return smoothstep(0, v, t) * smoothstep(1, v, t);
}
float dynamicDrops(float2 uv, float t)
{
float2 origUV = uv;
uv.y += t;
float2 scale = float2(6, 1);
float2 scale2 = scale * 2;
float2 id = floor(uv * scale2);
uv.y += noise(id.x);
id = floor(uv * scale2);
float3 n3 = noiseGrid(id);
float2 st = frac(uv * scale2) - float2(0.5, 0);
//计算水滴下落动画
float x = n3.x - 0.5;
//x方向添加扭曲
float wiggle = sin(origUV.y * 20 + sin(origUV.y + 20));
x += wiggle * (0.5 - abs(x)) * (n3.z - 0.5);
x *= 0.7;
//y方向添加随机速度
float y = pingPong(0.85, frac(t + n3.z));
float2 p = float2(x, y);
float d = length((st - p) * scale.yx);
//绘制水滴
float mainDrop = smoothstep(0.4, 0, d);
return mainDrop;
}

绘制拖尾的效果 : 拖尾 + 拖尾的前段 + 小水珠
1.绘制拖尾: 计算r,再次复用pingPang函数,并用sqrt使效果更平滑
再计算cd:拖尾的距离:abs(st.x - x),当前像素的水平纹理坐标 - 水滴在水平方向的目标位置,两个范围都在(-0.5, 0.5)的区间;
2.绘制拖尾的前段trail:基本上是(-0.02 - 0.02)的一个随机值

float dynamicDrops(float2 uv, float t)
{
//...上面保持一致
return mainDrop;
float r = sqrt(smoothstep(1, y, st.y));
float cd = abs(st.x - x);
float trail = smoothstep(0.23 * r, 0.15 * r * r, cd);
float trailFront = smoothstep(-0.02, 0.02, st.y - y);
trail *= trailFront * r * r;
return trail;
}
3.绘制小水珠: 调整随机分布的y, 计算当前像素与小水珠中心的距离dd,计算小水珠的droplets.
4.将小水珠的强度与水滴主体叠加,生成最终效果

float dynamicDrops(float2 uv, float t)
{
//..一直保持不变
//绘制拖尾的小水珠
y = origUV.y;
y = frac(y * 10) + (st.y - 0.5);
float dd = length(st - float2(x, y));
float droplets = smoothstep(0.3, 0, dd) * r * trailFront;
float m = mainDrop + droplets;
return float2(m, trail);
}
11.反复重新叠加进行效果计算
float2 drops(float2 uv, float t)
{
float sd = staticDrops(uv, t) * layer0;
float2 dd0 = dynamicDrops(uv, t) * layer1;
float2 dd1 = dynamicDrops(uv * 1.85, t) * layer2;
float c = sd + dd0.x + dd1.x;
c = smoothstep(0.3, 1, c);
float trail = max(dd0.y, dd1.y);
return float2(c, trail);
}

fixed4 frag (v2f i) : SV_Target
{
float2 uv = i.uv;
uv.x *= _ScreenParams.x / _ScreenParams.y;
float t = _Time.x * 20;
float layer0 = smoothstep(0, 2, _RainAmount);
float layer1 = smoothstep(0, 0.75, _RainAmount);
float layer2 = smoothstep(0, 0.5, _RainAmount);
float2 c = drops(uv, t, layer0, layer1, layer2);
return c.x;
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
return col;
}
12.增加几个layer层控制雨滴的数量
13.计算法向量 normal,表示雨滴表面的梯度。
fixed4 frag (v2f i) : SV_Target
{
float2 uv = i.uv;
//在长宽比为1:1上进行绘制,矫正
uv.x *= _ScreenParams.x / _ScreenParams.y;
//让圆点随时间移动而移动
float t = _Time.x * _Speed;
float layer0 = smoothstep(0, 2, _RainMount);
float layer1 = smoothstep(0, 0.75, _RainMount);
float layer2 = smoothstep(0, 0.5, _RainMount);
float2 c = drops(uv, t, layer0, layer1, layer2);
float2 e = float2(0.002, 0);
// 定义一个小的偏移量 e,用于计算法向量。
float cx = drops(uv + e, t, layer0, layer1, layer2).x;
// 在水平方向偏移 e 后,调用 drops 函数,获取偏移后的结果,并取 .x 分量。
// cx 表示水平方向偏移后的雨滴属性值。
float cy = drops(uv + e.yx, t, layer0, layer1, layer2).x;
// 在垂直方向偏移 e.yx(即 float2(0, 0.002))后,调用 drops 函数,获取偏移后的结果,并取 .x 分量。
// cy 表示垂直方向偏移后的雨滴属性值。
float2 normal = float2(cx - c.x, cy - c.x);
// 计算法向量 normal,表示雨滴表面的梯度。
// cx - c.x 是水平方向的变化率,cy - c.x 是垂直方向的变化率。
float focus = lerp(6, 0, c.y * _RainMount);
// 计算焦点值 focus,用于模拟模糊效果。
// lerp(6, 0, c.y * _RainAmount) 根据 c.y 和 _RainAmount 的值在 6 和 0 之间插值
fixed4 col = tex2D(_MainTex, float4(i.uv + normal, 0, focus));
// 根据法向量 normal 调整纹理坐标,从主纹理 _MainTex 中采样颜色。
// i.uv + normal 表示根据法向量偏移后的纹理坐标。
// focus 用于控制模糊效果。
return col;
}
这是最终效果

以下是完整代码
Shader "Gsyy/Raindop"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_RainAmount ("_RainAmount", float) = 1.0
_Speed ("_Speed", float) = 1.0
_Blur ("_Blur", Range(0, 1)) = 1.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Cull[_CullMode]
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _RainAmount;
float _Speed;
float _Blur;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
float pingPong(float v, float t)
{
return smoothstep(0, v, t) * smoothstep(1, v, t);
}
//伪随机数/噪声模式 分两步
//1.打乱2维uv,扩展成三维
//2.算上自己的点积,最后提取小数部分
float3 noiseGrid(float2 uv)
{
float pt = uv.x * 0.12234 + uv.y * 0.54321;//打乱uv
float3 pt3 = frac(float3(pt * 0.91234, pt * 0.8723, pt * 0.7654));//将uv扩展成3维向量
pt3 += dot(pt3, pt3.yzx);//将 dot(pt3,pt3.yzx) 的结果加到 pt3 的每个分量上:
pt3 = frac(float3((pt3.x + pt3.y) * pt3.z, (pt3.y + pt3.z) * pt3.x, (pt3.z + pt3.x) * pt3.y));//提取每一个分量的小数部分,否则过亮导致曝光
return pt3;
}
//一维噪声
float noise(float n)
{
return frac(sin(n * 12345.678) * 87654.321);
}
//静态水滴
float staticDrops(float2 uv, float t)
{
uv *= 10;
float2 id = floor(uv);//取uv整数
float3 n3 = noiseGrid(float2(id.x * 123.456, id.y * 456.789));
uv = frac(uv) - 0.5;//改变画布亮度
float2 p = (n3.xy - 0.5) * 0.7;//防止部分圆形小点被裁切,做位移和缩放
//为了得到静态水滴需要的圆形结构
float radius = length(uv - p);//条形结构 - 随机数网格
float fade = pingPong(0.025, frac(t + n3.z));
float c = smoothstep(0.3, 0, radius);// * frac(n3.z * 10) * fade;//让原点灰度随机变化
return c;
}
//动态水滴
float2 dynamicDrops(float2 uv, float t)
{
float2 origUV = uv;
uv.y += t;
float2 scale = float2(6, 1);
float2 scale2 = scale * 2;
float2 id = floor(uv * scale2);
uv.y += noise(id.x);
id = floor(uv * scale2);
float3 n3 = noiseGrid(id);
float2 st = frac(uv * scale2) - float2(0.5, 0);
//float d = length(st);
//计算水滴下落动画
float x = n3.x - 0.5;
//x方向添加扭曲
float wiggle = sin(origUV.y * 20 + sin(origUV.y + 20));
x += wiggle * (0.5 - abs(x)) * (n3.z - 0.5);
x *= 0.7;
//y方向添加随机速度
float y = pingPong(0.85, frac(t + n3.z));
float2 p = float2(x, y);
float d = length((st - p) * scale.yx);
//绘制水滴
float mainDrop = smoothstep(0.4, 0, d);
//绘制拖尾
float r = sqrt(smoothstep(1, y, st.y));
float cd = abs(st.x - x);
float trail = smoothstep(0.23 * r, 0.15 * r * r, cd);
float trailFront = smoothstep(-0.02, 0.02, st.y - y);
trail *= trailFront * r * r;
//绘制拖尾的小水珠
y = origUV.y;
y = frac(y * 10) + (st.y - 0.5);
float dd = length(st - float2(x, y));
float droplets = smoothstep(0.3, 0, dd) * r * trailFront;
float m = mainDrop + droplets;
return float2(m, trail);
}
//静态水滴 + 动态水滴(往下滑动的水滴,往下落,不连续的水珠)
float2 drops(float2 uv, float t, float layer0, float layer1, float layer2)
{
float sd = staticDrops(uv, t) * layer0;
float2 dd0 = dynamicDrops(uv, t) * layer1;
float2 dd1 = dynamicDrops(uv * 1.85, t) * layer2;
float c = sd + dd0.x + dd1.x;
c = smoothstep(0.3, 1, c);
float trail = max(dd0.y, dd1.y);
return float2(c, trail);
}
fixed4 frag (v2f i) : SV_Target
{
float2 uv = i.uv;
//在长宽比为1:1上进行绘制,矫正
uv.x *= _ScreenParams.x / _ScreenParams.y;
//让圆点随时间移动而移动
float t = _Time.x * _Speed;
float layer0 = smoothstep(0, 2, _RainAmount);
float layer1 = smoothstep(0, 0.75, _RainAmount);
float layer2 = smoothstep(0, 0.5, _RainAmount);
float2 c = drops(uv, t, layer0, layer1, layer2);
float2 e = float2(0.002, 0);
float cx = drops(uv + e, t, layer0, layer1, layer2).x;
float cy = drops(uv + e.yx, t, layer0, layer1, layer2).x;
float2 normal = float2(cx - c.x, cy - c.x);
//return c.x;
float focus = lerp(6, 0, _RainAmount);
focus *= _Blur;
fixed4 col = tex2Dlod(_MainTex, float4(i.uv + normal, 0, focus));
return col;
}
ENDCG
}
}
}
4831

被折叠的 条评论
为什么被折叠?



