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
        }
    }
}
相关推荐
め.1 天前
UIFramework
ui·unity
fqbqrr1 天前
2606d,用d语言构建游戏引擎
游戏引擎·d
℡枫叶℡1 天前
Unity - Import Activity Window 资源导入诊断信息窗口
unity·资源导入诊断
TO_ZRG1 天前
Unity 证书校验
unity·游戏引擎
mxwin1 天前
Unity Shader 切线空间数据是如何计算出来的
unity·游戏引擎·shader
mxwin2 天前
Unity Shader 法线贴图跟切线空间有什么关系
unity·游戏引擎·贴图·shader
mxwin2 天前
Unity Shader 贴图和采样的关系 如何保证贴图清晰
unity·游戏引擎·贴图·shader
心前阳光2 天前
Unity之使用火山引擎实现文字提问流式回复
unity·游戏引擎·火山引擎
mxwin2 天前
Unity Shader 什么是球谐光照 原理是什么
unity·游戏引擎·shader