|
上一篇文章(草地的顶点动画实现风吹草动)中我们通过模型的顶点动画来模拟了风吹草动的效果。今天我们在给他加上一个交互的效果,就是有角色或者其他物体在草地上走过时,草地会向周围散开。我们先看下草不动的时候向周围散开的样子。
其实实现方法也很简单,只要向Shader传递一下物体的坐标信息,然后在shader中用草的顶点坐标减去传递进来的物体坐标就可以得到草每个顶点散开的方向了。然后再通过一个半径的范围值来控制交互影响的范围,当然还是要通过UV的V方向的值来控制根部不动。- v2f vert (appdata v)
- {
- v2f o;
- float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
- //得到物体和草模型顶点之间的距离
- float dis = distance(_PlayerPos, worldPos);
- //通过影响范围,还有强度和UV的值来控制草的弯曲强度
- float pushDown = saturate((1 - dis + _PushRadius) * v.uv.y * _Strength);
- //计算出每个顶点散开的方向并做归一化处理
- float3 direction = normalize(worldPos.xyz - _PlayerPos.xyz);
- //减弱一些y轴向上的影响,否则很多草顶点会穿过地面
- direction.y *= 0.5;
- worldPos.xyz += direction * pushDown;
- o.pos = mul(UNITY_MATRIX_VP, worldPos);
- o.uv = v.uv;
- return o;
- }
复制代码 是不是很简单,接下来我们再把这个加在之前的风吹草动上面就行了。shader代码如下:- Shader "Custom/GrassVertexAniInteractive"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- _Noise("Noise", 2D) = "black" {}
- _WindControl("WindControl(x:XSpeed y:YSpeed z:ZSpeed w:windMagnitude)",vector) = (1,1,1,0.5)
- //前面几个分量表示在各个轴向上自身摆动的速度, w表示摆动的强度
- _WaveControl("WaveControl(x:XSpeed y:YSpeed z:ZSpeed w:worldSize)",vector) = (1,0,1,1)
- //前面几个分量表示在各个轴向上风浪的速度, w用来模拟地图的大小,值越小草摆动的越凌乱,越大摆动的越整体
- //_PlayerPos("PlayerPos", vector) = (0,0,0,0)
- //物体的位置坐标,需要在运行时通过C#代码传入,所以这里注释掉,把这个参数作为全局控制的参数
- _Strength("Strength", float) = 1
- //草地弯曲的强度
- _PushRadius("PushRadius", float) = 1
- //交互的范围
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #pragma multi_compile_instancing
- #include "UnityCG.cginc"
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- UNITY_VERTEX_INPUT_INSTANCE_ID
- };
- struct v2f
- {
- float2 uv : TEXCOORD0;
- float4 pos : SV_POSITION;
- };
- sampler2D _MainTex;
- sampler2D _Noise;
- half4 _WindControl;
- half4 _WaveControl;
- float4 _PlayerPos;
- half _Strength;
- half _PushRadius;
- v2f vert (appdata v)
- {
- v2f o;
- UNITY_SETUP_INSTANCE_ID(v);
- //草地自身风吹草动的计算
- float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
- float2 samplePos = worldPos.xz / _WaveControl.w;
- samplePos += _Time.x * -_WaveControl.xz;
- fixed waveSample = tex2Dlod(_Noise, float4(samplePos, 0, 0)).r;
- worldPos.x += sin(waveSample * _WindControl.x) * _WaveControl.x * _WindControl.w * v.uv.y;
- worldPos.z += sin(waveSample * _WindControl.z) * _WaveControl.z * _WindControl.w * v.uv.y;
- //草地交互的计算
- float dis = distance(_PlayerPos, worldPos);
- float pushDown = saturate((1 - dis + _PushRadius) * v.uv.y * _Strength);
- float3 direction = normalize(worldPos.xyz - _PlayerPos.xyz);
- direction.y *= 0.5;
- worldPos.xyz += direction * pushDown;
- o.pos = mul(UNITY_MATRIX_VP, worldPos);
- o.uv = v.uv;
- return o;
- }
- fixed4 frag (v2f i) : SV_Target
- {
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
复制代码 在C# 代码中我们只要在Update里面把物体的位置传递给Shader 的_PlayerPos参数就行了- void Update()
- {
- playerPos = transform.position;
- Shader.SetGlobalVector("_PlayerPos" , playerPos);
- }
复制代码 最后如果是大面积的草地顶点数量很多的时候,可以写两份Shader,一个是带交互的,一个是不带交互的。然后把草地分块再加上Trigger,默认都使用不带交互的Shader,当角色或物体进入到某块草地的Trigger时动态把这块草地的Shader替换成带交互的,这样Trigger以外的顶点都不会参与计算了。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|