实现草地Shader的物体互动效果

发布时间 2023-10-31 11:25:30作者: IDEA_W

我跟着教程:https://zhuanlan.zhihu.com/p/433385999 用Unity Shader实现了草地效果。

接下来我分享一下我在这篇文章的基础上实现简单的草地互动效果的经验。

1、从脚本中传入参数

如果需要实现互动效果,需要实现脚本向shader的传递参数。
脚本使用下面的代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//将这个组件拖给物体可以使其与草地互动
//互动范围是物体postion为中心的球体
public class MeshInteractWithGrass : MonoBehaviour
{
    void Update()
    {
        Shader.SetGlobalVector("_PositionMoving", transform.position);
    }
}

在场景中创建一个胶囊体,挂上这个脚本。

2、参数传入Shader并验证

在最初寻找传参的方法的时候,如何验证shader确实收到了参数是一个很重要的问题。

首先在shader 的CG代码段添加变量:

Vector _PositionMoving;//由脚本传入

我开始的时候在Shader的Properties中添加了_PositionMoving("Pos", Vector) = (0, 0, 0, 0)变量,但是我发现这样似乎无法正确接收到变量。
所以脚本中使用Shader.SetGlobalVector传入数据的时候,shader中不要再添加Properties了。
直接在CG代码段里面添加变量就好。

然后找到片元着色器 frag,把 frag 内部其他代码都注释掉,添加下面的代码:

//测试物体互动,测试数据是否传进去了
return(_PositionMoving);

随后运行游戏。移动物体,查看草叶的颜色是否有变化。
如果有变化,说明物体的位置数据通过脚本成功的传进了Shader中。

image

image

3、实现草叶顶点偏转

3.1、原理

请先根据文章开头的文章完成草地的实现

可以在生成草叶顶点的时候,对顶点施加一个用向量控制的偏移,来实现草叶偏转而草根不动。

教程BIRP - Grass Geometry Shader with Interactivity | Patreon中有简单提到实现原理,但没有说清楚细节。

在Shader的Properties中添加下面两个变量,用于控制互动范围和强度:

//其他物体与草的互动范围
_Radius("Radius", Float) = 1
//互动强度
_Strength("Strength", Float) = 1

在CG代码段添加:

//物体互动
float _Radius;
float _Strength;

这个教程的草地实现的时候,在几何着色器使用了一个循环用于细分草叶顶点。生成顶点的时候使用了函数GenerateGrassVertex。我们接下来的改动都在这个函数中进行。

在这个函数localPosition的定义后添加下面的代码:

//计算物体偏移
float3 ObjectPos = mul(unity_WorldToObject, _PositionMoving.xyz);
float3 dis = distance(ObjectPos, localPosition);
float3 radius = 1 - saturate(dis / _Radius);
float3 sphereVector = localPosition - ObjectPos;
sphereVector *= radius;
sphereVector = sphereVector.xyz * _Strength;
localPosition += sphereVector;

我尝试在模型空间下计算偏移。

  • 首先将传入的物体坐标转换到模型空间下。
  • 然后计算物体与新生成的顶点的距离。
  • 然后计算距离与_Radius变量之间的比例
  • 用顶点坐标向量减去物体坐标,得到从物体位置指向草叶顶点位置的向量sphereVector
  • sphereVector乘radius,实现不同距离有不同的向量大小
  • sphereVector乘_Strength调整偏移力度大小
  • localPosition加上得到的向量sphereVector实现偏移

这样就形成了以物体为中心的球形偏移:
image