Unity Shader 窗前雨滴效果

窗前雨滴效果(截图)

在这里插入图片描述

实现过程

0. 栅格化
float aspectRatio = 4.0;//每一行雨滴的宽高比
float tileNum = 5;//平铺数量
uv *= fixed2(tileNum * aspectRatio,tileNum);//栅格化uv
uv = frac(uv);
uv -=0.5;
1. 绘制主要雨滴
float r = length(uv);
r = smoothstep(0.2,0.1,r);
return float2(r,0.);
uv.y *= aspectRatio;
2. 绘制尾迹
//添加尾迹
float tailTileNum = 3.0;
float2 tailUV =uv *  float2(1.0,tailTileNum);
tailUV.y = frac(tailUV.y) - 0.5;
tailUV.x *= tailTileNum;
float rtail = length(tailUV);
rtail = smoothstep(0.2,0.1,rtail);
3. 尾迹塑形
//在雨滴上面总共有
float rtail = length(tailUV);
//尾迹塑形
rtail *= uv.y;//上面的y值大 使得雨滴形状变小
rtail = smoothstep(0.2,0.1,rtail);
//切除掉大雨滴下面的部分
rtail *= smoothstep(0.2,0.3,uv.y);//0.2以下的部分雨滴太大,切掉
4. 融合大雨滴和尾迹,并给雨滴添加模拟法线
float2 allUV = float2(rtail*tailUV+r*uv);
5. 把雨滴法线用于采样背景贴图
fixed4 finalColor = tex2D(_MainTex, uv + Rain(uv)*2.);
6. 让整个雨滴动起来
fixed4 finalColor = tex2D(_MainTex, uv + Rain(uv)*2.);
7. 让雨滴滚动起来更有节奏
//这里是屏幕空间
uv.y += time * PI2 /period / tileNum *0.45* 0.55;//加点y轴移动
//other code

float period = 5;//second per circle
float t = time * PI2 /period;
//这里是格子空间
//此处uv值范围为(-0.5,0.5)
uv.y += sin(t+sin(t+sin(t)*0.55))*0.45;
uv.y *= aspectRatio;
8. 加点基于格子的随机值
fixed2 idRand = Rand22(floor(uv));
t += idRand.x * PI2;//添加Y随机值
/
uv.x += (idRand.x-.5)*.6;//添加x轴随机偏移
9. 添加斜率的变化,模拟风的效果
flaot DEG2RAD = 3.14159 /180;
flaot ratoteDeg = 20.0 * DEG2RAD;
float s = sin(ratoteDeg);
float c = cos(ratoteDeg);
float2x2 rot = float2x2(c, -s, s, c);
uv = mul(rot,uv);
10.多加几层不同大小的雨滴
rainUV += Rains(uv,152.12,moveSpd);
rainUV += Rains(uv*2.32, 25.23, moveSpd);

C#源码

using UnityEngine;

public class ScreenRain : MonoBehaviour
{
    [Range(0,1)]
    public float blend = 1;

    [Range(0, 1)]
    public float moreRainAmount = 1;

    public bool wipe = true;

    [Range(0.1f, 20f)]
    public float wipeSizeX = 0.8f;

    [Range(0.1f, 20f)]
    public float wipeSizeY = 8.5f;

    public bool debugWipe = false;

	private Material mtrl = null;

    private int srcTexPropId = 0; 
    private int blendPropId = 0;
    private int wipeRTPropId = 0;
    private int wipeRTCanvasPropId = 0;
    private int wipeRTCanvas2PropId = 0;
    private int wipeScreenPosPropId = 0;
    private int wipeScreenSizePropId = 0;
    private int wipeSizePropId = 0;
    private int moreRainAmountPropId = 0;

    private RenderTexture wipeRT = null;
    private RenderTexture wipeRTCanvas = null;
    private RenderTexture wipeRTCanvas2 = null;
    private Vector2 wipeScreenPos;

    private void Awake()
    {
        mtrl = new Material(Shader.Find("Hidden/ScreenRain"));

        srcTexPropId = Shader.PropertyToID("_SrcTex");
        blendPropId = Shader.PropertyToID("_Blend");
        wipeRTPropId = Shader.PropertyToID("_WipeTex");
        wipeRTCanvasPropId = Shader.PropertyToID("_WipeCanvasTex");
        wipeRTCanvas2PropId = Shader.PropertyToID("_WipeCanvas2Tex");
        wipeScreenPosPropId = Shader.PropertyToID("_WipeScreenPos");
        wipeScreenSizePropId = Shader.PropertyToID("_WipeScreenSize");
        wipeSizePropId = Shader.PropertyToID("_WipeSize");
        moreRainAmountPropId = Shader.PropertyToID("_MoreRainAmount");

        wipeScreenPos = Vector2.one * -9000;
    }

    private void Update()
    {
        if(wipe)
        {
            if(Input.GetMouseButton(0))
            {
                //if (camera.gameObject.Instance.SetCamRainState == 0) {
                    //return;
                //}
                wipeScreenPos = Input.mousePosition;
            }
            else
            {
                wipeScreenPos = Vector2.one * -9000;
            }
        }
    }

	private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if(mtrl == null || mtrl.shader == null || !mtrl.shader.isSupported)
        {
            enabled = false;
            return;
        }

        if(wipe)
        {
            if(wipeRT == null || !wipeRT.IsCreated())
            {
                DestroyWipeRT();
                wipeRT = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.Default);
                Graphics.Blit(Texture2D.blackTexture, wipeRT);
            }
            if(wipeRTCanvas == null || !wipeRTCanvas.IsCreated())
            {
                DestroyWipeRTCanvas();
                wipeRTCanvas = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.Default);
                wipeRTCanvas2 = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.Default);
                Graphics.Blit(Texture2D.blackTexture, wipeRTCanvas);
            }
            mtrl.SetTexture(wipeRTPropId, wipeRT);
            mtrl.SetTexture(wipeRTCanvasPropId, wipeRTCanvas);
            mtrl.SetTexture(wipeRTCanvas2PropId, wipeRTCanvas2);
            mtrl.SetVector(wipeScreenPosPropId, wipeScreenPos);
            mtrl.SetVector(wipeScreenSizePropId, new Vector4(Screen.width, Screen.height, 0, 0));
            mtrl.SetVector(wipeSizePropId, new Vector4(wipeSizeX, wipeSizeY, 0, 0));
            Graphics.Blit(null, wipeRT, mtrl, 2);
            Graphics.Blit(null, wipeRTCanvas, mtrl, 3);
            Graphics.Blit(null, wipeRTCanvas2, mtrl, 4);
            Graphics.Blit(wipeRTCanvas2, wipeRTCanvas);
            mtrl.EnableKeyword("WIPE");
        }
        else
        {
            DestroyWipeRT();
            DestroyWipeRTCanvas();
            mtrl.DisableKeyword("WIPE");
        }

        mtrl.SetFloat(moreRainAmountPropId, moreRainAmount);
        mtrl.SetTexture(srcTexPropId, src);
        mtrl.SetFloat(blendPropId, blend);
        int rtSizeScale = 1;
#if UNITY_EDITOR
        rtSizeScale = 2;
#else
        rtSizeScale = 3; // 性能更好
#endif
        RenderTexture srcRT = RenderTexture.GetTemporary(src.width / rtSizeScale, src.height / rtSizeScale, 0, src.format);
        RenderTexture destRT = RenderTexture.GetTemporary(srcRT.width, srcRT.height, 0, srcRT.format);
        Graphics.Blit(src, srcRT);
        Graphics.Blit(srcRT, destRT, mtrl, 0);
        Graphics.Blit(destRT, dest, mtrl, 1);
        RenderTexture.ReleaseTemporary(srcRT);
        RenderTexture.ReleaseTemporary(destRT);
    }

#if UNITY_EDITOR
    // Debug
    private void OnGUI()
    {
        if(debugWipe)
        {
            if(wipeRT != null)
            {
                GUI.DrawTexture(new Rect(0,0,wipeRT.width / 4, wipeRT.height/4), wipeRT, ScaleMode.ScaleAndCrop, false);
            }
            if(wipeRTCanvas != null)
            {
                GUI.DrawTexture(new Rect(0,wipeRTCanvas.height/4,wipeRTCanvas.width / 4, wipeRTCanvas.height/4), wipeRTCanvas, ScaleMode.ScaleAndCrop, false);
            }
        }
    }
#endif

    private void OnDestroy()
    {
        if(mtrl != null)
        {
            DestroyImmediate(mtrl);
            mtrl = null;
        }

        DestroyWipeRT();
        DestroyWipeRTCanvas();
    }

    private void OnDisable()
    {
        DestroyWipeRT();
        DestroyWipeRTCanvas();
    }

    private void DestroyWipeRT()
    {
        if(wipeRT != null)
        {
            Destroy(wipeRT);
            wipeRT = null;
        }
    }

    private void DestroyWipeRTCanvas()
    {
        if(wipeRTCanvas != null)
        {
            Destroy(wipeRTCanvas);
            wipeRTCanvas = null;
        }
        if(wipeRTCanvas2 != null)
        {
            Destroy(wipeRTCanvas2);
            wipeRTCanvas2 = null;
        }
    }
}

Shader源码

Shader "Hidden/ScreenRain"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Cull Off ZWrite Off ZTest Always

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 3.0
			#pragma multi_compile _ WIPE
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float4 scrPos : TEXCOORD0;
			};

			sampler2D _MainTex;
			sampler2D _SrcTex;
			sampler2D _WipeCanvasTex;

			uniform half _MoreRainAmount;

			float3 N13(float p) {
				float3 p3 = frac(float3(p,p,p) * float3(.1031,.11369,.13787));
				p3 += dot(p3, p3.yzx + 19.19);
				return frac(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
			}
			float4 N14(float t) {
				return frac(sin(t*float4(123., 1024., 1456., 264.))*float4(6547., 345., 8799., 1564.));
			}
			float N(float t) {
				return frac(sin(t*12345.564)*7658.76);
			}
			float Saw(float b, float t) {
				return smoothstep(0., b, t)*smoothstep(1., b, t);
			}
			float2 DropLayer2(float2 uv, float t) {
				float2 UV = uv;
				
				uv.y += t*0.75;
				float2 a = float2(6., 1.);
				float2 grid = a*2.;
				float2 id = floor(uv*grid);
				
				float colShift = N(id.x); 
				uv.y += colShift;
				
				id = floor(uv*grid);
				float3 n = N13(id.x*35.2+id.y*2376.1);
				float2 st = frac(uv*grid)-float2(.5, 0);
				
				float x = n.x-.5;
				
				float y = UV.y*20.;
				float wiggle = sin(y+sin(y));
				x += wiggle*(.5-abs(x))*(n.z-.5);
				x *= .7;
				float ti = frac(t+n.z);
				y = (Saw(.85, ti)-.5)*.9+.5;
				float2 p = float2(x, y);
				
				float d = length((st-p)*a.yx);
				
				float mainDrop = smoothstep(.4, .0, d);
				
				float r = sqrt(smoothstep(1., y, st.y));
				float cd = abs(st.x-x);
				float trail = smoothstep(.23*r, .15*r*r, cd);
				float trailFront = smoothstep(-.02, .02, st.y-y);
				trail *= trailFront*r*r;
				
				y = UV.y;
				float trail2 = smoothstep(.2*r, .0, cd);
				float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z;
				y = frac(y*10.)+(st.y-.5);
				float dd = length(st-float2(x, y));
				droplets = smoothstep(.3, 0., dd);
				float m = mainDrop+droplets*r*trailFront; 
				
				return float2(m, trail);
			}
			float StaticDrops(float2 uv, float t) {
				uv *= 40.;
				
				float2 id = floor(uv);
				uv = frac(uv)-.5;
				float3 n = N13(id.x*107.45+id.y*3543.654);
				float2 p = (n.xy-.5)*.7;
				float d = length(uv-p);
				
				float fade = Saw(.025, frac(t+n.z));
				float c = smoothstep(.3, 0., d)*frac(n.z*10.)*fade;
				return c;
			}
			float2 Drops(float2 uv, float t, float l0, float l1, float l2) {
				float s = StaticDrops(uv, t)*l0; 
				float2 m1 = DropLayer2(uv, t)*l1;
				float2 m2 = DropLayer2(uv*1.85, t)*l2;
				
				float c = s+m1.x+m2.x;
				c = smoothstep(.3, 1., c);
				
				return float2(c, max(m1.y*l0, m2.y*l1));
			}

			float2 DropsDynamic(float2 uv, float t, float l1, float l2)
			{
				float2 m1 = DropLayer2(uv, t)*l1;
				float2 m2 = DropLayer2(uv*1.75, t)*l2;
				
				float c = m1.x+m2.x;
				c = smoothstep(.4, 1., c);
				
				return float2(c, max(0, m2.y*l1));
			}

			v2f vert (appdata v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.scrPos = ComputeScreenPos(o.pos);
				return o;
			}

			fixed4 frag (v2f _iParam) : SV_Target
			{
				float2 fragCoord = (_iParam.scrPos.xy / _iParam.scrPos.w) * _ScreenParams.xy;
				float4 fragColor = 0;
        		
				float2 uv = (fragCoord.xy - .5*_ScreenParams.xy) / _ScreenParams.y;
				float2 UV = fragCoord.xy / _ScreenParams.xy;
				float3 M = 2;
				float T = _Time.y + M.x*2.;

				float t = T*(.2+0.1*_MoreRainAmount);

				float rainAmount = M.y;

				uv *= 0.5;

				float staticDrops = smoothstep(-.5, 1., rainAmount)*2.;
				float layer1 = smoothstep(.25, .75, rainAmount);
				float layer2 = smoothstep(.0, .5, rainAmount);

				float2 n = float2(0, 0);
				float2 c = Drops(uv, t, staticDrops, layer1, layer2);
				float2 e = float2(.001, 0.);
				float cx = Drops(uv + e, t, staticDrops, layer1, layer2).x;
				float cy = Drops(uv + e.yx, t, staticDrops, layer1, layer2).x;
				n += float2(cx - c.x, cy - c.x);
				float moreRainAmount = 1.25 + 1.25 * _MoreRainAmount;
				for(float i = 1.25; i < moreRainAmount; i+=0.25)
				{
					float2 _c = DropsDynamic(uv, t*i, layer1, layer2);
					float _cx = DropsDynamic(uv + e, t*i, layer1, layer2).x;
					float _cy = DropsDynamic(uv + e.yx, t*i, layer1, layer2).x;
					n += float2(_cx - _c.x, _cy - _c.x);
				}

				float blend = (n.x + n.y)*(1.75 + _MoreRainAmount);

#if defined(WIPE)
				float wipe = tex2D(_WipeCanvasTex, UV).r;
				wipe = saturate(pow(wipe, 0.2));
				float3 col = tex2D(_MainTex, UV + n * (1 - wipe)).rgb;
#else
				float3 col = tex2D(_MainTex, UV + n).rgb;
#endif

				fragColor = float4(col, blend);

				return fragColor;
			}
			ENDCG
		}

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

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}
			
			sampler2D _SrcTex;
			sampler2D _MainTex;
			half _Blend;

			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 rainC = tex2D(_MainTex, i.uv);
				fixed4 mainC = tex2D(_SrcTex, i.uv);
				fixed blend = rainC.a * _Blend;
				mainC.rgb = rainC.rgb * blend + mainC.rgb * (1-blend);
				return mainC;
			}
			ENDCG
		}

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			float4 _WipeScreenPos;
			float4 _WipeScreenSize;
			float4 _WipeSize;

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

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				float2 currScreenPos = i.uv * _WipeScreenSize;
				float4 wipeSize = float4(100,100,0,0) * _WipeSize.xyzw;
				wipeSize.xy = float2(1,1) / float2(dot(wipeSize.xz,wipeSize.xz), dot(wipeSize.yw,wipeSize.yw));
				float4 radius = float4(_WipeScreenPos.xy - currScreenPos.xy, 0, 0);
				radius.xy = float2(dot(radius.xz,radius.xz), dot(radius.yw,radius.yw));
				float inside = 1 - min(dot(radius.xy, wipeSize.xy), 1);
				return inside;
			}
			ENDCG
		}

		Pass
		{
			Blend One One

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			sampler2D _WipeTex;

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

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				return tex2D(_WipeTex, i.uv);
			}
			ENDCG
		}

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			sampler2D _WipeCanvasTex;

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

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}

			float4 frag (v2f i) : SV_Target
			{
				float4 c = tex2D(_WipeCanvasTex, i.uv);
				return c - 0.005f;
			}
			ENDCG
		}
	}
}

  • 10
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值