Unity Shader 窗前雨滴效果
程序员文章站
2024-02-12 22:23:46
...
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
}
}
}
上一篇: destoon官方标签大全
下一篇: php环境筹建