Unity制作下雨中的地面效果

Unity引擎制作下雨效果

大家好,我是阿赵。

之前介绍了Unity引擎里面通过UV偏移做序列帧动画的做法,这里再介绍一个进阶的用法,模拟地面下雨的雨点效果。

一、原理

最基本的原理,还是基于这个序列帧动画的做法。不过这里做一点改变。我不再用网格的UV作为计算的UV,而是通过worldPosition的xz轴去计算,并且,我加上了一个frac方法。

复制代码
float2 uv = frac(i.worldPos.xz*_tiling);
uv = GetSequenceAnimUV(uv,_cols,_rows,_speed, _startFrame);

这样做的好处是,UV不再依赖网格模型,可以平均的铺在地面上,而且地面可以无限延伸,特别适合做地面雨滴效果。

这样做的效果是这样的:

由于frac的效果是把数值只保留小数部分,所以之前的uv坐标,就被划分成很多个小的0-1之间的区域。于是UV序列帧动画,也变成了多个。

接下来要做的事情,就是把这张1-9的数字图片,换成一张雨点扩散的序列帧图片。由于如果是真的水面,固有色部分还需要做其他效果,所以这个雨点的序列图最好是法线贴图。

然后通过之前介绍过的法线贴图的用法,把序列帧动画的UV采样法线贴图,然后把法线的效果加强,就出现了雨点打地面的效果。

二、代码

于这里只是介绍雨点的效果,所以固有色我就不去认真做了,只是做了个固有色,然后雨点的法线效果是通过高光来表现的。组合了一下之前介绍过的光照模型的代码,还有序列帧动画的代码,就得到了这么一个shader代码了。

复制代码
Shader "azhao/Rain"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_color("Color",Color) = (1,1,1,1)
		_cols("Cols",int) = 1
		_rows("Rows",int) = 1
		_tiling("Tiling",int) = 1
		_speed("Speed",float) = 25
		_startFrame("startFrame",int) = 0
		_NormalTex("Normal Tex", 2D) = "black"{}
		_normalScale("normalScale", Range(0 , 1)) = 0
		_specColor("SpecColor",Color) = (1,1,1,1)
		_shininess("shininess", Range(1 , 100)) = 1
		_specIntensity("specIntensity",Range(0,1)) = 1

    }
    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;
				float3 normal:NORMAL;
				float3 tangent:TANGENT;
            };

            struct v2f
            {
                
                float4 vertex : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 worldPos:TEXCOORD1;
				float3 worldNormal : TEXCOORD2;
				float3 worldTangent :TEXCOORD3;
				float3 worldBitangent : TEXCOORD4;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			float4 _color;
			float _cols;
			float _rows;
			float _tiling;
			float _speed;
			float _startFrame;
			sampler2D _NormalTex;
			float4 _NormalTex_ST;
			float _normalScale;
			float4 _specColor;
			float _shininess;
			float _specIntensity;
			float _ambientIntensity;


			float2 GetSequenceAnimUV(float2 uv,float cols,float rows,float speed,float startFrame)
			{
				float totalTiles = cols * rows;

				float colsOffset = 1.0f / cols;
				float rowsOffset = 1.0f / rows;
				float speedVal = _Time.y * speed;
				float2 offsetTiling = float2(colsOffset, rowsOffset);
				float currentIndex = round(fmod(speedVal + startFrame, totalTiles));
				currentIndex += (currentIndex < 0) ? totalTiles : 0;
				float lineNum = round(fmod(currentIndex, cols));
				float offsetX = lineNum * colsOffset;
				float rowCount = round(fmod((currentIndex - lineNum) / cols, rows));
				rowCount = (int)(rows - 1) - rowCount;
				float offsetY = rowCount * rowsOffset;
				float2 offsetXY = float2(offsetX, offsetY);
				float2 result = uv*offsetTiling +offsetXY;
				return result;
			}

			//简化版的转换法线并缩放的方法
			half3 UnpackScaleNormal(half4 packednormal, half bumpScale)
			{
				half3 normal;
				//由于法线贴图代表的颜色是0到1,而法线向量的范围是-1到1
				//所以通过*2-1,把色值范围转换到-1到1
				normal = packednormal * 2 - 1;
				//对法线进行缩放
				normal.xy *= bumpScale;
				//向量标准化
				normal = normalize(normal);
				return normal;
			}

			//获取HalfLambert漫反射值
			float GetHalfLambertDiffuse(float3 worldPos, float3 worldNormal)
			{
				float3 lightDir = UnityWorldSpaceLightDir(worldPos);
				float NDotL = saturate(dot(worldNormal, lightDir));
				float halfVal = NDotL * 0.5 + 0.5;
				return halfVal;
			}

			//获取BlinnPhong高光
			float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
			{
				float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
				float specDir = max(dot(normalize(worldNormal), halfDir), 0);
				float specVal = pow(specDir, _shininess);
				return specVal;
			}

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldTangent = UnityObjectToWorldDir(v.tangent);
				o.worldBitangent = cross(o.worldNormal, o.worldTangent);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
				float2 uv = frac(i.worldPos.xz*_tiling);
				float2 normalUV = GetSequenceAnimUV(uv,_cols,_rows,_speed, _startFrame);

				//采样法线贴图的颜色
				half4 normalCol = tex2D(_NormalTex, normalUV);
				//得到切线空间的法线方向
				half3 normalVal = UnpackScaleNormal(normalCol, _normalScale).rgb;

				//构建TBN矩阵
				float3 tanToWorld0 = float3(i.worldTangent.x, i.worldBitangent.x, i.worldNormal.x);
				float3 tanToWorld1 = float3(i.worldTangent.y, i.worldBitangent.y, i.worldNormal.y);
				float3 tanToWorld2 = float3(i.worldTangent.z, i.worldBitangent.z, i.worldNormal.z);

				//通过切线空间的法线方向和TBN矩阵,得出法线贴图代表的物体世界空间的法线方向
				float3 worldNormal = float3(dot(tanToWorld0, normalVal), dot(tanToWorld1, normalVal), dot(tanToWorld2, normalVal));

				//用法线贴图的世界空间法线,算漫反射
				half diffuseVal = GetHalfLambertDiffuse(i.worldPos, worldNormal);

				//用法线贴图的世界空间法线,算高光角度
				half3 specCol = _specColor * GetBlinnPhongSpec(i.worldPos, worldNormal)*_specIntensity;

                half4 col = tex2D(_MainTex,i.uv);
				col.rgb = col.rgb*_color.rgb + specCol;
                return col;
            }
            ENDCG
        }
    }
}
相关推荐
一个笔记本7 小时前
godot log | 修改main scene
游戏引擎·godot
nnsix9 小时前
Unity PicoVR开发 实时预览Unity场景 在Pico设备中(串流)
unity·游戏引擎
一只一只15 小时前
Unity之UGUI Button按钮组件详细使用教程
unity·游戏引擎·ugui·button·ugui button
神米米17 小时前
Maya快速安装UE4 布料权重绘制插件PhysX导出apx
游戏引擎·ue4·maya
WarPigs18 小时前
Unity阴影
unity·游戏引擎
一只一只18 小时前
Unity之Invoke
unity·游戏引擎·invoke
技术小甜甜20 小时前
【Godot】【入门】信号系统从 0 到 1(UI/玩法彻底解耦的通用写法)
ui·游戏引擎·godot
技术小甜甜21 小时前
【Godot】【入门】节点生命周期怎么用(避免帧循环乱写导致卡顿的范式)
游戏引擎·godot
tealcwu21 小时前
【Unity踩坑】Simulate Touch Input From Mouse or Pen 导致检测不到鼠标点击和滚轮
unity·计算机外设·游戏引擎
ThreePointsHeat1 天前
Unity WebGL打包后启动方法,部署本地服务器
unity·游戏引擎·webgl