Unity透明水滴玻璃效果

水滴分为 静态水滴 + 动态水滴 + 往下落的不连续水珠(圆点水珠+拖尾)

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));
    //..其余保持不变
}
  1. 绘制水滴,将求到的圆限制在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
        }
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值