Unity大面积草地渲染——2、草地的动态交互

目录
1、Shader控制一棵草的渲染
2、草地的动态交互
3、使用GPUInstancing渲染大面积的草
4、对大面积草地进行区域剔除和显示等级设置

大家好,我是阿赵。
这里继续讲大面积草地渲染的第二个部分,草地动态交互。这里主要有风吹效果和球体碰撞效果2种。

一、风吹效果

Unity使用shader控制草的渲染和动画


风吹动草的效果,主要还是使用顶点程序来控制顶点的偏移
回顾一下之前的基础草的shader的顶点程序

v2f vert (appdata v)
            {
                v2f o;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;

				o.pos = UnityObjectToClipPos(v.vertex+float3(vVertexOffset.x, hVertexOffset, vVertexOffset.y));
                return o;
            }

在这个顶点程序里面,已经使用顶点偏移来实现了草的受重力弯曲的形态,所以现在只需要对它进行一些小修改,加入一个风力的影响,就可以实现左右摇摆的效果了。
这里我添加一个风的方向,还有一个Sin曲线来模拟风的左右摇摆:

float2 wind = _windOffset * hVal*_SinTime.w;
float2 windPosXZOffset = vVertexOffset + wind;
o.pos = UnityObjectToClipPos(v.vertex + float3(windPosXZOffset.x, hVertexOffset, windPosXZOffset.y));

在这里插入图片描述
在这里插入图片描述

通过调节windOffset,可以让草摆动的幅度产生变化

修改之后的完整shader是这样的:

Shader "azhao/GrassWind"
{
    Properties
    {
		_MainTex("MainTex", 2D) = "white" {}
		_hmin("hmin", Range(0 , 1)) = 0
		_hmax("hmax", Range(0 , 1)) = 1
		_hOffset("hOffset", Range(-1 , 1)) = 0
		_vmin("vmin", Range(0 , 1)) = 0
		_vmax("vmax", Range(0 , 1)) = 1
		_vOffset("vOffset", Range(-5 , 5)) = 0
		_topCol("topCol", Color) = (0,1,0,0)
		_windOffset("windOffset", Vector) = (0,0,0,0)
		_bottomCol("bottomCol", Color) = (0,0,0,0)

    }
    SubShader
    {
		Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
		Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


			#include "UnityShaderVariables.cginc"
			#pragma target 3.0
            #include "UnityCG.cginc"

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

            };

            struct v2f
            {                
                float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
            };

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float _hOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;

            v2f vert (appdata v)
            {
                v2f o;

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;


				float2 windPosXZOffset = vVertexOffset + wind;

				o.pos = UnityObjectToClipPos(v.vertex+float3(windPosXZOffset.x, hVertexOffset, windPosXZOffset.y));
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {

                // sample the texture
                half4 col = tex2D(_MainTex, i.uv);
				half3 finalCol = col.rgb * _topCol.rgb*i.hvVal.z + col.rgb;
				finalCol = clamp(finalCol*i.hvVal.x + _bottomCol * (1 - i.hvVal.x)*finalCol,  half3(0, 0, 0), half3(1, 1, 1));
				half alpha = col.a;
				clip(alpha - 0.5);
                return half4(finalCol,alpha);
            }
            ENDCG
        }
		
		//为了产生影子,加多一个pass
		Pass {
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

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

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

			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
			};

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float _hOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;

			v2f vert(appdata v)
			{
				v2f o;

				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;
				float2 windPosXZOffset = vVertexOffset + wind;
				o.pos = UnityObjectToClipPos(v.vertex + float3(windPosXZOffset.x, hVertexOffset, windPosXZOffset.y));
				return o;
			}

			half4 frag(v2f i) : SV_Target
			{

				// sample the texture
				half4 col = tex2D(_MainTex, i.uv);
				clip(col.a - 0.5);
				return col;
			}
			ENDCG

		}

    }
}

二、和球的交互

unity草地互动


这里说的和球交互,是因为我不想花太多的时间去放一个角色模型进去并控制,所以我就简单的用一个球来代替。其实是模拟了一个人在草丛里面行走时,对草的交互。
既然是草被人挤开的动画,那么还是使用顶点动画来做了
在这里插入图片描述

这里需要定义一下球对草地的影响范围
在这里插入图片描述

假设球的坐标可以输入到shader里面,那么求出草的顶点和球坐标的距离,就可以过滤出离球比较近的一圈才会受到球的影响。
在这里插入图片描述

在刚才的顶点程序上面,再添加一点代码:

float roleDis = (1 - distance(o.worldPos.xz, rolePos.xz));
float2 roleNor = (o.worldPos.xz - rolePos.xz)*step(0, roleDis)*(roleDis*_roleMul);
float2 rolePosXZOffset = vVertexOffset + wind * (1 - roleNor) + roleNor * hVal;
float rolePosYOffset = hVertexOffset - saturate(roleDis*_roleHOffset);
o.pos = UnityObjectToClipPos(v.vertex+float3(rolePosXZOffset.x, rolePosYOffset, rolePosXZOffset.y));
return o;

需要注意:
1、rolePos不需要在shader的Properties里面声明,它是一个全局的变量,在C#代码里面,使用Shader.SetGlobalVector(“rolePos”, role.transform.position);来给它赋值
2、这里模拟的是一个角色的影响,如果是多个角色在草地里面移动,可以把rolePos全局变量变成数组,具体的实现可以自己试试。

这时候顶点的控制就受到了3个方面的影响:
1、重力
2、风力
3、球体的碰撞

完整的Shader:

Shader "azhao/GrassWindBall"
{
    Properties
    {
		_MainTex("MainTex", 2D) = "white" {}
		_hmin("hmin", Range(0 , 1)) = 0
		_hmax("hmax", Range(0 , 1)) = 1
		_hOffset("hOffset", Range(-1 , 1)) = 0
		_vmin("vmin", Range(0 , 1)) = 0
		_vmax("vmax", Range(0 , 1)) = 1
		_vOffset("vOffset", Range(-5 , 5)) = 0
		_topCol("topCol", Color) = (0,1,0,0)
		_windOffset("windOffset", Vector) = (0,0,0,0)
		_bottomCol("bottomCol", Color) = (0,0,0,0)
		_roleMul("roleMul", Range(0 , 10)) = 0
		_roleHOffset("roleHOffset", Range(0 , 10)) = 0
    }
    SubShader
    {
		Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
		Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

			#pragma target 3.0
            #include "UnityCG.cginc"

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

            struct v2f
            {                
                float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
            };

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float3 rolePos;
			uniform float _roleMul;
			uniform float _hOffset;
			uniform float _roleHOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;
			SamplerState sampler_MainTex;

            v2f vert (appdata v)
            {
                v2f o;

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;
				float roleDis = (1 - distance(o.worldPos.xz, rolePos.xz));
				float2 roleNor = (o.worldPos.xz - rolePos.xz)*step(0, roleDis)*(roleDis*_roleMul);
				float2 rolePosXZOffset = vVertexOffset + wind * (1 - roleNor) + roleNor * hVal;
				float rolePosYOffset = hVertexOffset - saturate(roleDis*_roleHOffset);
				o.pos = UnityObjectToClipPos(v.vertex+float3(rolePosXZOffset.x, rolePosYOffset, rolePosXZOffset.y));
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half4 col = tex2D(_MainTex, i.uv);
				half3 finalCol = col.rgb * _topCol.rgb*i.hvVal.z + col.rgb;
				finalCol = clamp(finalCol*i.hvVal.x + _bottomCol * (1 - i.hvVal.x)*finalCol,  half3(0, 0, 0), half3(1, 1, 1));
				half alpha = col.a;
				clip(alpha - 0.5);
                return half4(finalCol,alpha);
            }
            ENDCG
        }
		
		//为了产生影子,加多一个pass
		Pass {
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

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

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

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;
			};

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;
			uniform float2 _windOffset;
			uniform float3 rolePos;
			uniform float _roleMul;
			uniform float _hOffset;
			uniform float _roleHOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;
			SamplerState sampler_MainTex;

			v2f vert(appdata v)
			{
				v2f o;

				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;
				float2 wind = _windOffset * hVal*_SinTime.w;
				float roleDis = (1 - distance(o.worldPos.xz, rolePos.xz));
				float2 roleNor = (o.worldPos.xz - rolePos.xz)*step(0, roleDis)*(roleDis*_roleMul);
				float2 rolePosXZOffset = vVertexOffset + wind * (1 - roleNor) + roleNor * hVal;
				float rolePosYOffset = hVertexOffset - saturate(roleDis*_roleHOffset);
				o.pos = UnityObjectToClipPos(v.vertex + float3(rolePosXZOffset.x, rolePosYOffset, rolePosXZOffset.y));
				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				// sample the texture
				half4 col = tex2D(_MainTex, i.uv);

				clip(col.a - 0.5);
				return col;
			}
			ENDCG

		}

    }
}

猜你喜欢

转载自blog.csdn.net/liweizhao/article/details/130569925