Unity 雨水滴到屏幕效果

2023-10-29


前言

本文主要介绍用unity实现雨水滴到屏幕的效果,文章介绍的是基础实现,读完这篇文章再去实现复杂效果会更得心应手些。我们先看更高级效果的图片:

在这里插入图片描述

一、实现过程

1.代码

先直接上代码,后面再做介绍:

Shader "Unlit/ScreenWater"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_Size("Size", Range(0, 100)) = 1.0
		_T("Time", Float) = 1.0
		_Distortion("Distortion", Float) = -5
		_Blur("Blur", Range(0, 1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #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;
			half _Size;
			half _T;
			half _Distortion;
			half _Blur;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
			
			// 求伪随机数
			half N21(half2 p){
				p = frac(p * half2(123.34, 345.45));
				p += dot(p, p + 34.345);
				return frac(p.x + p.y);
			}	
			
			half3 layer(half2 UV, half T){
				half t = fmod( _Time.y + T, 3600);
				half4 col = half4(0, 0, 0, 1.0);
				half aspect = half2(2, 1);
				half2 uv = UV * _Size * aspect;
				uv.y += t * 0.25;
				half2 gv = frac(uv) - 0.5;//-0.5,调整原点为中间
				half2 id = floor(uv);
				half n = N21(id); // 0 1
				t += n * 6.2831; //2PI

				half w = UV.y * 10;
				half x = (n - 0.5) * 0.8;
				x += (0.4 - abs(x)) * sin(3 * w) * pow(sin(w), 6) * 0.45;
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;
				y -= (gv.x - x) * (gv.x - x);
				half2 dropPos = (gv - half2(x, y)) / aspect; //- half2(x,y) 为了移动
				half drop = smoothstep(0.05, 0.03, length(dropPos));

				half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
				trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8;
				half trail = smoothstep(0.03, 0.01, length(trailPos));
				half fogTrail = smoothstep(-0.05, 0.05, dropPos.y);// 拖尾小水滴慢慢被拖掉了
				fogTrail *= smoothstep(0.5, y, gv.y);// 拖尾小水滴渐变消失
				fogTrail *= smoothstep(0.05, 0.04, abs(dropPos.x));
				trail *= fogTrail;
				//col += fogTrail * 0.5;
				//col += trail;
				//col += drop;
				//if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
				half2 offset = drop * dropPos +  trail * trailPos;
				return half3(offset, fogTrail);
			}

            half4 frag (v2f i) : SV_Target
            {
				half3 drops = layer(i.uv, _T);
				drops += layer(i.uv * 1.25 + 7.52, _T);
				drops += layer(i.uv * 1.35 + 1.54, _T);
				drops += layer(i.uv * 1.57 - 7.52, _T);			
				half blur = _Blur * 7 * (1 - drops.z);
				half4 col = tex2Dlod(_MainTex, half4(i.uv + drops.xy * _Distortion, 0, blur));
                return col;
            }
            ENDCG
        }
    }
}

下图显示了上面那段代码的效果,读者可自行在unity中先贴这段代码看,后面会做详细解析:

在这里插入图片描述

2.代码分步解析

下面是实现步骤:

【1】先将uv平铺多次,为后面实现多个雨滴效果。

直接贴片元着色器代码:

            half4 frag (v2f i) : SV_Target
            {
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(1, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间
				col.rg = gv;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

下图显示了上面那段代码的效果:

在这里插入图片描述
说明:A为从左下角的原点调整为中间的情况, _Size 为平铺多次的情况,读者可边看边在untiy上操作。

half2 aspect = half(2, 1);//长宽比

设置长宽比为2:1后,得到效果:

在这里插入图片描述

【2】实现多个雨滴效果

直接贴片元着色器代码:

            half4 frag (v2f i) : SV_Target
            {
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间

				half drop = smoothstep(0.05, 0.03, length(gv));//画圆, length(gv),可以理解为半径
				col += drop;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

下图显示了上面那段代码的效果:

在这里插入图片描述
先附上smoothstep的解析,后续有时间会发文介绍glsl相关函数的理解,以及如何使用这些函数去实现复杂的效果。以下是smoothstep的解析:
我们先打开网址 Graphic Caculate,输入如图所示:

在这里插入图片描述
smoothstep的公式就是s = k^2*(3 - 2k), 图上所示就是smoothstep(0.4, 0.6, x), 如果是反过来就是smoothstep(0.6, 0.4, x), 得到的就是如图所示:

在这里插入图片描述
我们可以直接在ShaderToy上演示输入如图所示代码:

在这里插入图片描述
0.2 ->0.0 可以理解为简单的反向操作如图所示:

在这里插入图片描述
【4】水滴移动
直接贴片元着色器代码:

            half4 frag (v2f i) : SV_Target
            {
            	half t = _Time.y;
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间
				
				half x = 0;
				half y = sin(t);//水珠是上下来回移动的
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect
				
				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				col += drop;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

改动下代码,让水滴是往下移动的:

            half4 frag (v2f i) : SV_Target{
             	half t = _Time.y;
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间
				
				half x = 0;
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect
				
				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				col += drop;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

读者需要按照步骤去实现,更能理解算法表现的效果,帮助理解。上述代码中(2)得到的函数图是:

在这里插入图片描述
读者可自行控制算法,调出更好的效果。

【4】水滴拖尾效果,拖尾水滴滑下
1、先画一个拖尾水滴,代码:

            half4 frag (v2f i) : SV_Target{
             	half t = _Time.y;
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间
				
				half x = 0;
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect
				
				half trail = smoothstep(0.03, 0.01, length(gv));//拖尾小水滴

				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				col += drop;
				col += trail;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

下图显示了上面那段代码的效果:

在这里插入图片描述
2、再计算移动,代码:

            half4 frag (v2f i) : SV_Target{
             	half t = _Time.y;
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间
				
				half x = 0;
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect
				
				half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
				trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8; // -0.5是将半圆调整为完整圆
				half trail = smoothstep(0.03, 0.01, length(trailPos));//拖尾小水滴

				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				col += drop;
				col += trail;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

下图显示了上面那段代码的效果:

在这里插入图片描述
3、接下来我们将下部分拖尾水滴清理掉,边移动拖尾边消失,代码:

            half4 frag (v2f i) : SV_Target{
             	half t = _Time.y;
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间
				
				half x = 0;
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect
				
				half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
				trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8; // -0.5是将半圆调整为完整圆
				half trail = smoothstep(0.03, 0.01, length(trailPos));//拖尾小水滴

				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				trail *= smoothstep(-0.05, 0.05, dropPos.y);//将下部分拖尾水滴清理掉,边移动拖尾边消失
				col += drop;
				col += trail;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

下图显示了上面那段代码的效果:

在这里插入图片描述
3、然后将水滴做个渐变,从上到下从无到有,代码:

				... ...
				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				trail *= smoothstep(-0.05, 0.05, dropPos.y);//将下部分拖尾水滴清理掉,边移动拖尾边消失
				trail *= smoothstep(0.5, y, gv.y);//将水滴做个渐变,从上到下从无到有, 0.5是以gv为坐标的,顶部0.5,底部-0.5
				col += drop;
				col += trail;
				... ...

下图显示了上面那段代码的效果:

在这里插入图片描述
【5】水滴做偏移,得到一种失真的效果,这时我们需要同时对x,y做偏移
1、同样,先贴上简单的效果,先改变下水滴的形状,代码:

            half4 frag (v2f i) : SV_Target{
				... ...				
				half x = 0;
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
				y -= gv.x * gv.x;//改变水滴形状
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect				
				... ...               
				return col;
            }

下图显示了上面那段代码的效果:

在这里插入图片描述
2、对x做偏移,代码如下:

            half4 frag (v2f i) : SV_Target{
             	half t = _Time.y;
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间
				
				half w = i.uv.y * 10;
				half x = sin(3 * w) * pow(sin(w), 6) * 0.45;//对x做些复杂的移动,(1)
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
				y -= (gv.x - x) * (gv.x - x);
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect
				
				half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
				trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8; // -0.5是将半圆调整为完整圆
				half trail = smoothstep(0.03, 0.01, length(trailPos));//拖尾小水滴

				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				trail *= smoothstep(-0.05, 0.05, dropPos.y);//将下部分拖尾水滴清理掉,边移动拖尾边消失
				trail *= smoothstep(0.5, y, gv.y);//将水滴做个渐变,从上到下从无到有, 0.5是以gv为坐标的,顶部0.5,底部-0.5
				col += drop;
				col += trail;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
               
				return col;
            }

(1)中的函数图:

在这里插入图片描述

下图显示了上面那段代码的效果:

在这里插入图片描述

3、我们需要更随机些的效果,因此引入伪随机数:

			// 求伪随机数
			half N21(half2 p){
				p = frac(p * half2(123.34, 345.45));
				p += dot(p, p + 34.345);
				return frac(p.x + p.y);
			}	

先测试下伪随机数,代码:

				... ... 
				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
				col *= 0; col += N21(i.uv); //测试代码
				... ... 

下图显示了上面那段代码的效果:

在这里插入图片描述
可以得到噪点图,再把 col += N21(i.uv); 改为col += N21(id); 代码如下:

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
				col *= 0; col += N21(id);

下图显示了上面那段代码的效果:

在这里插入图片描述
我们调整时间处理代码:

        	half4 frag (v2f i) : SV_Target{
             	half t = fmod(_Time.y * _T, 7200);//每两个小时重置下,这样不会得到太大的数字,效果不平滑(3)
             	... ...
            }

(3)中如果数字太大会得到如图效果,效果很不好。

在这里插入图片描述
4、接下来我们完善下我们的效果,代码:


            half4 frag (v2f i) : SV_Target{
             	half t = fmod(_Time.y * _T, 7200);//每两个小时重置下,这样不会得到太大的数字,效果不平滑(3)
				half4 col = half4(0, 0, 0, 1.0);
				half2 aspect = half2(3, 1);//长宽比
				half2 uv = i.uv * _Size * aspect;
				uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
				half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间

				half2 id = floor(uv);
				half n = N21(id); // 0 1
				t += n * 6.2831; //2PI

				half w = i.uv.y * 10;
				half x = (n - 0.5) * 0.8;; // -0.4 - 0.4
				x += (0.4 - abs(x)) * sin(3 * w) * pow(sin(w), 6) * 0.45;// 0.4- abs(x), 目的是限制在边框内摆动
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
				y -= (gv.x - x) * (gv.x - x);
				half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect
				
				half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
				trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8; // -0.5是将半圆调整为完整圆
				half trail = smoothstep(0.03, 0.01, length(trailPos));//拖尾小水滴

				half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
				trail *= smoothstep(-0.05, 0.05, dropPos.y);//将下部分拖尾水滴清理掉,边移动拖尾边消失
				trail *= smoothstep(0.5, y, gv.y);//将水滴做个渐变,从上到下从无到有, 0.5是以gv为坐标的,顶部0.5,底部-0.5
				col += drop;
				col += trail;

				if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
				//col *= 0; col += N21(id);
               
				return col;
            }

下图显示了上面那段代码的效果:

在这里插入图片描述
【6】水滴滑动拖尾雾效果
1、先实现第一步效果,代码:

			half4 frag(v2f i) : SV_Target{
			  half t = fmod(_Time.y * _T, 7200);//每两个小时重置下,这样不会得到太大的数字,效果不平滑(3)
			  half4 col = half4(0, 0, 0, 1.0);
			  half2 aspect = half2(3, 1);//长宽比
			  half2 uv = i.uv * _Size * aspect;
			  uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
			  half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间

			  half2 id = floor(uv);
			  half n = N21(id); // 0 1
			  t += n * 6.2831; //2PI

			  half w = i.uv.y * 10;
			  half x = (n - 0.5) * 0.8;; // -0.4 - 0.4
			  x += (0.4 - abs(x)) * sin(3 * w) * pow(sin(w), 6) * 0.45;// 0.4- abs(x), 目的是限制在边框内摆动
			  half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
			  y -= (gv.x - x) * (gv.x - x);
			  half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect

			  half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
			  trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8; // -0.5是将半圆调整为完整圆
			  half trail = smoothstep(0.03, 0.01, length(trailPos));//拖尾小水滴

			  half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
			  half fogTrail = smoothstep(-0.05, 0.05, dropPos.y);//将下部分拖尾水滴清理掉,边移动拖尾边消失
			  fogTrail *= smoothstep(0.5, y, gv.y);//将水滴做个渐变,从上到下从无到有, 0.5是以gv为坐标的,顶部0.5,底部-0.5
			  trail *= fogTrail;
			  col += fogTrail * 0.5;

			  col += drop;
			  col += trail;

			  if (gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
			  //col *= 0; col += N21(id);

			  return col;
			}

下图显示了上面那段代码的效果:

在这里插入图片描述
2、我们需要讲上图的两边雾的效果缩小下,代码:


			  half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
			  half fogTrail = smoothstep(-0.05, 0.05, dropPos.y);//将下部分拖尾水滴清理掉,边移动拖尾边消失
			  fogTrail *= smoothstep(0.5, y, gv.y);//将水滴做个渐变,从上到下从无到有, 0.5是以gv为坐标的,顶部0.5,底部-0.5
			  fogTrail *= smoothstep(0.05, 0.04, abs(dropPos.x));// 两边雾的效果缩小下
			  trail *= fogTrail;
			  col += fogTrail * 0.5;

下图显示了上面那段代码的效果, 可以看出两边雾被缩小了:

在这里插入图片描述
【7】结合主纹理,得到简答的真实水滴屏幕效果
1、直接贴代码:

			half4 frag(v2f i) : SV_Target{
			  half t = fmod(_Time.y * _T, 7200);//每两个小时重置下,这样不会得到太大的数字,效果不平滑(3)
			  half4 col = half4(0, 0, 0, 1.0);
			  half2 aspect = half2(3, 1);//长宽比
			  half2 uv = i.uv * _Size * aspect;
			  uv.y += t * 0.25; //水滴周期移动的同时,uv也往下移动,这样就能做到水滴往下移动的效果
			  half2 gv = frac(uv) - 0.5;//-0.5,将左下角的原点调整为中间

			  half2 id = floor(uv);
			  half n = N21(id); // 0 1
			  t += n * 6.2831; //2PI

			  half w = i.uv.y * 10;
			  half x = (n - 0.5) * 0.8;; // -0.4 - 0.4
			  x += (0.4 - abs(x)) * sin(3 * w) * pow(sin(w), 6) * 0.45;// 0.4- abs(x), 目的是限制在边框内摆动
			  half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;//将y改为更复杂些的移动(2)
			  y -= (gv.x - x) * (gv.x - x);
			  half2 dropPos = (gv - half2(x, y)) / aspect;// 移动水滴,除以aspect是因为之前的uv*aspect

			  half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
			  trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8; // -0.5是将半圆调整为完整圆
			  half trail = smoothstep(0.03, 0.01, length(trailPos));//拖尾小水滴

			  half drop = smoothstep(0.05, 0.03, length(dropPos));//画圆, length(gv),可以理解为半径
			  half fogTrail = smoothstep(-0.05, 0.05, dropPos.y);//将下部分拖尾水滴清理掉,边移动拖尾边消失
			  fogTrail *= smoothstep(0.5, y, gv.y);//将水滴做个渐变,从上到下从无到有, 0.5是以gv为坐标的,顶部0.5,底部-0.5
			  fogTrail *= smoothstep(0.05, 0.04, abs(dropPos.x));
			  trail *= fogTrail;
			  col += fogTrail * 0.5;

			  col += drop;
			  col += trail;

			  //if (gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
			  //col *= 0; col += N21(id);

			  half2 offset = drop * dropPos + trail * trailPos;
			  half4 finalColor = tex2D(_MainTex, i.uv + offset * _Distortion);

			  return finalColor;
			}

下图显示了上面那段代码的效果:

在这里插入图片描述
2、接下来我们需要对主纹理采样进行处理,得到一种雾屏的效果
代码:

			half2 offset = drop * dropPos + trail * trailPos;
			half blur = _Blur * 7 * (1 - fogTrail);//这里为啥1-fogTrail,读者可去试下,得到拖尾雾效果是相反的
			//直接将tex2D改为tex2dlod, _Blur控制模糊程度
			//tex2Dlod(ref: https://msdn.microsoft.com/en-us/library/windows/desktop/bb509680%28v=vs.85%29.aspx)
			half4 finalColor = tex2Dlod(_MainTex, half4(i.uv + offset * _Distortion, 0, blur ));

下图显示了上面那段代码的效果:

在这里插入图片描述
3、接下来我们需要整理下代码:

			half3 dropLayer(half2 UV, half T){
				half t = fmod( _Time.y + T, 3600);
				half4 col = half4(0, 0, 0, 1.0);
				half aspect = half2(2, 1);
				half2 uv = UV * _Size * aspect;
				uv.y += t * 0.25;
				half2 gv = frac(uv) - 0.5;//-0.5,调整原点为中间
				half2 id = floor(uv);
				half n = N21(id); // 0 1
				t += n * 6.2831; //2PI

				half w = UV.y * 10;
				half x = (n - 0.5) * 0.8;
				x += (0.4 - abs(x)) * sin(3 * w) * pow(sin(w), 6) * 0.45;
				half y = -sin(t + sin(t + sin(t) * 0.5)) * 0.45;
				y -= (gv.x - x) * (gv.x - x);
				half2 dropPos = (gv - half2(x, y)) / aspect; //- half2(x,y) 为了移动
				half drop = smoothstep(0.05, 0.03, length(dropPos));

				half2 trailPos = (gv - half2(x, t * 0.25)) / aspect; //- half2(x,y) 为了移动
				trailPos.y = (frac(trailPos.y * 8) - 0.5) / 8;
				half trail = smoothstep(0.03, 0.01, length(trailPos));
				half fogTrail = smoothstep(-0.05, 0.05, dropPos.y);// 拖尾小水滴慢慢被拖掉了
				fogTrail *= smoothstep(0.5, y, gv.y);// 拖尾小水滴渐变消失
				fogTrail *= smoothstep(0.05, 0.04, abs(dropPos.x));
				trail *= fogTrail;
				//col += fogTrail * 0.5;
				//col += trail;
				//col += drop;
				//if(gv.x > 0.48 || gv.y > 0.49) col = half4(1.0, 0, 0, 1.0); // 辅助线
				half2 offset = drop * dropPos +  trail * trailPos;
				return half3(offset, fogTrail);
			}

            half4 frag (v2f i) : SV_Target
            {
				half3 drops = dropLayer(i.uv, _T);		
				half blur = _Blur * 7 * (1 - drops.z);
				half4 col = tex2Dlod(_MainTex, half4(i.uv + drops.xy * _Distortion, 0, blur));
                return col;
            }

【8】现在的雨滴效果还不够,我们需要叠加几层,效果会更丰富些

         	half4 frag (v2f i) : SV_Target
            {
				half3 drops = dropLayer(i.uv, _T);
				//这里的参数读者可自行测试控制
				drops += dropLayer(i.uv * 1.25 + 7.52, _T);
				drops += dropLayer(i.uv * 1.35 + 1.54, _T);
				drops += dropLayer(i.uv * 1.57 - 7.52, _T);			
				half blur = _Blur * 7 * (1 - drops.z);
				half4 col = tex2Dlod(_MainTex, half4(i.uv + drops.xy * _Distortion, 0, blur));
                return col;
            }

上面得到的效果就是开篇的那种效果了,读者还可以结合抓屏效果去实现更好的效果,性能上这块算法也没比较复杂的指令运算,移动端可以使用的,读者液可自行使用renderdoc等GPU测试工具测试下即可。

总结

本文实现的雨水滴到屏幕效果相对来说还是不够丰富的,如果读者想实现更复杂的效果可以参考 raindropShaderToy链接
通过学习笔者写的,再去理解更复杂的效果会容易上手些。后续笔者会继续推出有详细解析的文章,码字不易,觉得写的不错的可以点赞关注支持下,激励下笔者,谢谢啦。

参考

raindrop
ShaderToy链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity 雨水滴到屏幕效果 的相关文章

  • GLSL 中统一浮点行为和常量浮点行为的不同

    我正在尝试在 GLSL 中实现模拟双精度 并且观察到一种奇怪的行为差异 导致 GLSL 中出现细微的浮点错误 考虑以下片段着色器 写入 4 浮点纹理以打印输出 layout location 0 out vec4 Output unifor
  • OpenGL:始终相同的颜色

    我正在 Windows 上编写一个程序 使用c opengl 2 1 and SDL我在顶点颜色方面遇到了一些问题 我在用着glColor3f设置每个顶点集的颜色 但它似乎不起作用 无论我选择什么颜色 我都会将每个顶点绘制为红色 我检查了传
  • 延迟阴影映射 GLSL

    我目前正在实施延迟渲染管道 但我仍坚持使用阴影贴图 我已经成功地将其实施到前向管道中 我所做的步骤是 获取灯光视图中的位置 转换为光视图剪辑空间 使用 0 5 0 5 获取阴影纹理坐标 检查深度 编辑 使用新结果图像更新代码 float c
  • 按像素值偏移 gl_Position 或 gl_Vertex

    我的属性包含像素值 我想用这个属性值来偏移我的 gl vertex 问题是我的 gl vertex 以世界单位为单位 而 offset attribute 以像素为单位 如果我将屏幕尺寸作为统一发送 然后将像素转换为 1 到 1 值 并将其
  • 如何在opengl中使用四元数绕屏幕中心旋转?

    我正在尝试实现轨迹球 轨迹球旋转 但我的中心有问题 回转 无论如何 我希望中心成为屏幕的中心 让我解释一下到目前为止我所做的事情 我创建了一个四元数 旋转轴 向量起点x向量结束 角度 向量起点 向量结束 从该四元数中 我创建了一个旋转矩阵
  • 纹理采样:根据LOD值计算BIAS值

    GL ES 2 0 中的功能纹理2DLod在片段着色器中不可用 我需要移植 GLSL 着色器 在 GL ES 2 0 中我只能使用二维纹理 sampler2D 采样器 vec2 坐标 浮点数bias 告诉我如何计算 a 的值bias相当于已
  • 如何在多采样纹理上渲染帧缓冲区对象?

    我目前有一个使用多个通道的渲染引擎 其中图像的各个部分在纹理上渲染 然后使用着色器进行组合 它有效 现在我想激活多重采样 我在这里读到 http www opengl org wiki Framebuffer Object Examples
  • 带有 std::vector 的 VBO

    我用 C 和 OpenGL 编写了一个模型加载器 我用过std vectors 来存储我的顶点数据 但现在我想将其传递给glBufferData 但是数据类型却截然不同 我想知道是否有办法可以相互转换std vector至已记录的const
  • 使用 gl_FragColor 与 vec4 颜色?

    似乎有很多不明确的地方gl FragColor被弃用 例如 它缺失在GLSL 4 40 规范 https www khronos org registry OpenGL specs gl GLSLangSpec 4 40 pdf 但它包含在
  • OpenGL 与 OpenCL,选择哪个以及为什么?

    哪些功能使 OpenCL 能够独特地选择 OpenGL 和 GLSL 进行计算 尽管有与图形相关的术语和不实用的数据类型 OpenGL 是否有任何真正的警告 例如 可以通过使用其他纹理将 a 渲染到纹理来完成并行函数评估 减少操作可以通过迭
  • 之前对 GL.Color3 的调用使我的纹理使用了错误的颜色

    制作 2D OpenGL 游戏 渲染帧时 我需要首先绘制一些计算的四边形几何体 然后绘制一些纹理精灵 当我的渲染方法主体仅绘制精灵时 一切正常 但是 当我尝试在精灵之前绘制几何四边形时 精灵的纹理会更改为之前使用的最后一个 GL Color
  • Retina 显示屏中具有 QOpenGLWIdget 的 Qt MainWindow 显示错误大小

    我有一个 Qt 应用程序MainWindow 我嵌入一个QOpenGLWidget在里面 一切正常 直到我开始使用 Apple Retina 显示屏并在高 DPI 模式下运行我的应用程序 我的QOpenGLWidget只是它应该具有的大小的
  • OpenGL 缓冲区、glFlush 和 glutSwapBuffers()

    使用之间有什么区别吗 glutInitDisplayMode GLUT SINGLE GLUT RGB with glFlush and glutInitDisplayMode GLUT DOUBLE GLUT RGB with glutS
  • 如何创建自己的 openGL 上下文并将其绑定到 GLCanvas?

    所以当我开始掌握java时 paint Graphics g 我继续创建自己的渲染方法 但我必须了解缓冲区策略以及如何 获取 图形 所以现在我在学习openGL 我必须掌握方法 Override public void display GL
  • 如何在 OpenGL 中绘制镜像某些东西的镜子?

    根据我的理解 要在 OpenGL 中进行镜像 您基本上需要绘制场景 然后将所有内容翻转并再次绘制 只是使其通过镜子可见 从而在镜子中创建完美翻转的图像 但我看到的问题是 执行此操作时 唯一可以看到其他镜子的镜子是在前一个镜子之后渲染的镜子
  • 开启TK onRenderFrame和onUpdateFrame的区别?

    我目前正在使用 OpenTK 框架和 OpenGL 用 C 编写 Jump n Run 游戏 Open TK 提供预设功能 例如GameWindow Run or GameWindow onUpdateFrame onRenderFrame
  • 如何使用OpenGL数组纹理?

    我正在尝试在OpenGL中使用精灵表 通过数组纹理实现它这就是我加载纹理的方式 QImage image image load C QtProjects project images spritesheet png png const un
  • GLSL memoryBarrierShared() 有用吗?

    我想知道 memoryBarrierShared 的用处 事实上 当我查找屏障功能的文档时 我读到 对于计算着色器中任何给定的静态屏障实例 单个工作组内的所有调用都必须进入该实例 然后才能允许任何调用继续超出该实例 这确保了在给定的屏障静态
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前
  • 线性/非线性纹理映射扭曲的四边形

    In my 上一个问题 https stackoverflow com questions 10832909 quad strip texturing distortion 已经确定 当对四边形进行纹理化时 面被分解为三角形 并且纹理坐标以

随机推荐

  • elasticsearch之嵌套对象、父子文档

    一 嵌套对象 es并非关系型数据库 它并不擅长关系型数据库的join操作 在存储数据时 用冗余数据替代查询时的关联 如blog和comments 如果用关系型数据库 会将blog存一个表 comments存另一个表 然后将这两个表关联起来
  • 华兴数控g71外圆循环编程_数控车床加工时的复合循环指令G70,G71,G72,G73

    复合循环指令应用在切除非一次加工即能加工到规定尺寸的场合 主要在粗车和多次切螺纹的情况下使用 它主要有以下几种 外径 内径粗车循环指令G71 该指令将工件切削到精加工之前的尺寸 精加工前工件形状及粗加工的刀具路径由系统根据精加工尺寸自动设定
  • DataWorks数据埋点的设计及未来发展的思考

    什么是前端埋点 马总曾经说过现在是DT时代 大数据的时代 数据已经成为一家公司最宝贵的财富 越来越多的互联网公司开始重视数据的应用 数据应用的过程是 数据收集 gt 数据整理 数据同步 gt 数据分析 gt 数据可视化 前端埋点是用户行为数
  • ChatGPT的主要应用场景例子

    ChatGPT是一种基于深度学习技术的大型语言模型 它可以根据用户提供的输入信息 生成自然语言文本或响应 这种技术可以应用于很多领域 下面将详细介绍ChatGPT在以下几个方面的应用 以下是使用过程中的一些应用场景对话记录 欢迎补充更多的应
  • 什么是ioc

    什么叫ioc 1 ioc叫做控制反转 是面向对象的一种设计方式 2 把对象的创建和对象之间的调用过程 交给spring管理 3 目的 为了使耦合度降低 耦合度 我有多个service类 都需要调用一个dao类 当我修改这个dao类的位置时
  • Spark Streaming流式数据处理

    目录 一 Spark Streaming 简介 二 简单的例子 三 Spark Streaming相关核心类 3 1 StreamingContext 3 2 离散流 Discretized Streams DStreams 3 3 Inp
  • LiDAR Camera Calibration

    LiDAR和Camera的联合标定 目前有不少方法 不同方法适合不同的传感器 如果有必要 可以自己写一个联合标定的工具 不少公司会自己再写一个 因为标定之后精度可以更高 如果LiDAR是32 及其以上 适合使用Baidu Apollo的方法
  • java模式之装饰器模式

    定义 装饰器模式也叫作包装器模式 只在不改变原有对象的基础上 动态的给一个对象添加一些额外的职责 就增加功能来说 装饰器模式相比生成子类更为灵活 属于结构型设计模式 装饰器模式提供了比继承更有弹性的替代方案将功能附加到对象上 因此 装饰器模
  • 服务器数码管不显示,数码管常见故障及检修方法

    数码管不亮 产生此故障的原因可能有 1 变压器损坏 引线断开或虚焊 若变压器损坏 则予以更换或重新绕制 若引线断开或虚焊 则应重新连接或重新焊接 2 5V电源故障 应检查显示电路电源电压及数码管供电是否正常 正常时为5V直流电压 若两者不正
  • photoshop 去除水印的六种方法

    一 使用仿制图章工具去除文字这是比较常用的方法 具体的操作是 选取仿制图章工具 按住Alt键 在无文字区域点击相似的色彩名图案采样 然后在文字区域拖动鼠标复制以覆盖文字 要注意的是 采样点即为复制的起始点 选择不同的笔刷直径会影响绘制的范围
  • 我们常说的十三薪、十五薪这种,能百分之百发下来吗?它和年终奖有什么区别?

    大家在跳槽谈薪的时候 经常会听到公司说发十三薪 十五薪 这种薪资能百分之百发下来吗 它和年终奖有什么区别 来看下网友们怎么说吧 有人说 它们就是年终奖 十三薪是年终奖发一个月工资 十五薪就是年终奖发三个月工资 当然 也不一定百分之百发放 可
  • 博易大师交易系统改成自己的服务器6,博易大师行情交易软件,如何自定义页面设置?...

    可以根据个人需要对页面进行设置 以新建页面为例 具体步骤如下 1 点击左上角菜单栏中的 页面 新建页面 如图4 6 1 或者点击左上角系统页面中的 我的页面 新建页面 如图4 6 2 图4 6 1 2 2 在空白窗口中点击鼠标右键 选择 切
  • 【Java核心】JDK、JRE、 JVM的联系与区别

    个人简介 Java领域新星创作者 阿里云技术博主 星级博主 专家博主 正在Java学习的路上摸爬滚打 记录学习的过程 个人主页 29 的博客 学习社区 进去逛一逛 JDK JRE JVM的联系与区别 1 简述 2 是什么 3 联系和区别 1
  • Element-UI Methods 如何调用

    现在用 Element UI 做上传文件的功能 需要调用 clearFiles 方法 但尝试了几次不行 查了好久终于明白了 1 先在el upload 加个 ref xx 2 然后把这个写在对应的事件方法里面 this refs xx cl
  • 高效 MacBook 工作环境配置

    高效 MacBook 工作环境配置 作者 正鹏 隃墨 来源 http www xialeizhou com p 71 工欲善其事 必先利其器 工具永远都是用来解决问题的 没必要为了工具而工具 一切工具都是为了能快速准确的完成工作和学习任务而
  • 微信小程序第一次提交审核之 用户隐私保护指引设置

    微信小程序第一次提交审核之 用户隐私保护指引设置 微信小程序新规 提交的时候需要填写这个 用户隐私保护指引设置 填写方式如下图 参考图片如下
  • 使用ajax请求提交数据时,日期类型无法转换为JAVA中的日期类型

    在做毕业项目时碰见日期类型无法传到后台controller中的问题 因为springMVC中没有提供默认的日期转换器 前端页面传过来的日期类字符串无法转换为java中的日期类型 使用 DatetimeFormat注解完成转换 做一个笔记 前
  • 【压力测试 2】JMeter压力测试之Internal server error 500 问题解决思路

    一 JMeter客户端实现有两种方式 1 Java 选择压测时 链接是复用的 代码中的http调用都加了连接池 2 httpclient4 压测时 每请求一次都创建一个新的链接 jmeter5 0以前默认关闭了连接复用 5 0上是打开的 即
  • Hive 计算用户留存率(次日,3日,N日)

    文章目录 什么是用户留存率 创建数据源 计算留存率 计算 N 日的留存率 什么是用户留存率 用户留存率是指在特定时间段内 用户在使用某个产品或应用程序后 再次使用该产品或应用程序的比例 它可以帮助公司了解用户是否喜欢他们的产品 并提供有价值
  • Unity 雨水滴到屏幕效果

    文章目录 前言 一 实现过程 1 代码 2 代码分步解析 总结 参考 前言 本文主要介绍用unity实现雨水滴到屏幕的效果 文章介绍的是基础实现 读完这篇文章再去实现复杂效果会更得心应手些 我们先看更高级效果的图片 一 实现过程 1 代码