找回密码
 立即注册
查看: 1097|回复: 15

[笔记] Unity草地交互的实现

[复制链接]
发表于 2021-2-19 09:36 | 显示全部楼层 |阅读模式
上一篇文章(草地的顶点动画实现风吹草动)中我们通过模型的顶点动画来模拟了风吹草动的效果。今天我们在给他加上一个交互的效果,就是有角色或者其他物体在草地上走过时,草地会向周围散开。我们先看下草不动的时候向周围散开的样子。
其实实现方法也很简单,只要向Shader传递一下物体的坐标信息,然后在shader中用草的顶点坐标减去传递进来的物体坐标就可以得到草每个顶点散开的方向了。然后再通过一个半径的范围值来控制交互影响的范围,当然还是要通过UV的V方向的值来控制根部不动。
  1. v2f vert (appdata v)
  2.             {
  3.                 v2f o;
  4.                 float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
  5.                 //得到物体和草模型顶点之间的距离
  6.                 float dis = distance(_PlayerPos, worldPos);
  7.                 //通过影响范围,还有强度和UV的值来控制草的弯曲强度
  8.                 float pushDown = saturate((1 - dis + _PushRadius) * v.uv.y * _Strength);
  9.                 //计算出每个顶点散开的方向并做归一化处理
  10.                 float3 direction = normalize(worldPos.xyz - _PlayerPos.xyz);
  11.                 //减弱一些y轴向上的影响,否则很多草顶点会穿过地面
  12.                 direction.y *= 0.5;
  13.                 worldPos.xyz += direction * pushDown;
  14.                 o.pos = mul(UNITY_MATRIX_VP, worldPos);
  15.                 o.uv = v.uv;
  16.                 return o;
  17.             }
复制代码
是不是很简单,接下来我们再把这个加在之前的风吹草动上面就行了。shader代码如下:
  1. Shader "Custom/GrassVertexAniInteractive"
  2. {
  3.     Properties
  4.     {
  5.         _MainTex ("Texture", 2D) = "white" {}
  6.         _Noise("Noise", 2D) = "black" {}
  7.         _WindControl("WindControl(x:XSpeed y:YSpeed z:ZSpeed w:windMagnitude)",vector) = (1,1,1,0.5)
  8.         //前面几个分量表示在各个轴向上自身摆动的速度, w表示摆动的强度
  9.         _WaveControl("WaveControl(x:XSpeed y:YSpeed z:ZSpeed w:worldSize)",vector) = (1,0,1,1)
  10.         //前面几个分量表示在各个轴向上风浪的速度, w用来模拟地图的大小,值越小草摆动的越凌乱,越大摆动的越整体
  11.         //_PlayerPos("PlayerPos", vector) = (0,0,0,0)
  12.         //物体的位置坐标,需要在运行时通过C#代码传入,所以这里注释掉,把这个参数作为全局控制的参数
  13.         _Strength("Strength", float) = 1
  14.         //草地弯曲的强度
  15.         _PushRadius("PushRadius", float) = 1
  16.         //交互的范围
  17.     }
  18.     SubShader
  19.     {
  20.         Tags { "RenderType"="Opaque" }
  21.         LOD 100
  22.         Pass
  23.         {
  24.             CGPROGRAM
  25.             #pragma vertex vert
  26.             #pragma fragment frag
  27.             #pragma multi_compile_instancing
  28.             #include "UnityCG.cginc"
  29.             struct appdata
  30.             {
  31.                 float4 vertex : POSITION;
  32.                 float2 uv : TEXCOORD0;
  33.                 UNITY_VERTEX_INPUT_INSTANCE_ID
  34.             };
  35.             struct v2f
  36.             {
  37.                 float2 uv : TEXCOORD0;
  38.                 float4 pos : SV_POSITION;
  39.             };
  40.             sampler2D _MainTex;
  41.             sampler2D _Noise;
  42.             half4 _WindControl;
  43.             half4 _WaveControl;
  44.             float4 _PlayerPos;
  45.             half _Strength;
  46.             half _PushRadius;
  47.             v2f vert (appdata v)
  48.             {
  49.                 v2f o;
  50.                 UNITY_SETUP_INSTANCE_ID(v);
  51.                 //草地自身风吹草动的计算
  52.                 float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
  53.                 float2 samplePos = worldPos.xz / _WaveControl.w;
  54.                 samplePos += _Time.x * -_WaveControl.xz;
  55.                 fixed waveSample = tex2Dlod(_Noise, float4(samplePos, 0, 0)).r;
  56.                 worldPos.x += sin(waveSample * _WindControl.x) * _WaveControl.x * _WindControl.w * v.uv.y;
  57.                 worldPos.z += sin(waveSample * _WindControl.z) * _WaveControl.z * _WindControl.w * v.uv.y;
  58.                 //草地交互的计算
  59.                 float dis = distance(_PlayerPos, worldPos);
  60.                 float pushDown = saturate((1 - dis + _PushRadius) * v.uv.y * _Strength);
  61.                 float3 direction = normalize(worldPos.xyz - _PlayerPos.xyz);
  62.                 direction.y *= 0.5;
  63.                 worldPos.xyz += direction * pushDown;
  64.                 o.pos = mul(UNITY_MATRIX_VP, worldPos);
  65.                 o.uv = v.uv;
  66.                 return o;
  67.             }
  68.             fixed4 frag (v2f i) : SV_Target
  69.             {
  70.                 fixed4 col = tex2D(_MainTex, i.uv);
  71.                 return col;
  72.             }
  73.             ENDCG
  74.         }
  75.     }
  76. }
复制代码
在C# 代码中我们只要在Update里面把物体的位置传递给Shader 的_PlayerPos参数就行了
  1. void Update()
  2.     {
  3.         playerPos = transform.position;
  4.         Shader.SetGlobalVector("_PlayerPos" , playerPos);
  5.     }
复制代码
最后如果是大面积的草地顶点数量很多的时候,可以写两份Shader,一个是带交互的,一个是不带交互的。然后把草地分块再加上Trigger,默认都使用不带交互的Shader,当角色或物体进入到某块草地的Trigger时动态把这块草地的Shader替换成带交互的,这样Trigger以外的顶点都不会参与计算了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-2-19 09:45 | 显示全部楼层
如果是多个角色都需要扰动草的话,,有什么方法可以一次计算出来吗
发表于 2021-2-19 09:53 | 显示全部楼层
那就要把多个角色的position通过数组传递给shader,然后在shader里面把草地交互计算那一段放在for循环里面来处理
发表于 2021-2-19 09:58 | 显示全部楼层
如果使用了gpu instancing 是否还有效? 比如用drawmeshinstanceIndirected
发表于 2021-2-19 10:07 | 显示全部楼层
也是可以的,如果用DrawMeshInstanceIndirected的话需要配合ComputeBuffer和ComputeShader
发表于 2021-2-19 10:16 | 显示全部楼层
offset写到纹理里
发表于 2021-2-19 10:20 | 显示全部楼层
塞尔达就是怎么实现的[惊喜]
发表于 2021-2-19 10:29 | 显示全部楼层
请问当我把草放大后,物体对草的影响程度就会非常低有什么解决方案么?
发表于 2021-2-19 10:33 | 显示全部楼层
把相应的影响范围和弯曲强度的值加大
发表于 2021-2-19 10:40 | 显示全部楼层
调大之后感觉更多是鱼眼镜头效果儿不是草被压倒
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-5-6 20:07 , Processed in 0.097977 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表