laojiong 发表于 2021-10-20 14:03:09

草(二)Unity移动端可用的草海,Culling&LOD

## Culling

> 根据相机视椎体对视椎体外的草进行裁剪,从而优化数据量。
> 具体实现可以简述为,把草的位置坐标转换到相机裁剪空间,得到float4的clipPos,因为裁剪空间可视范围内的坐标范围在[-w,w]之间,所以通过裁剪空间的坐标点数据可以判断草是否在视野中。

***GrassComputeScript***中加入如下代码

```csharp
...
private int[] argsBufferReset = new int[] { 0, 1, 0, 0 };
//相机
private Camera m_MainCamera;
...

private void OnEnable() {
      // 如果已经初始化,先清理旧数据
      if (m_Initialized) {
            OnDisable();
      }
        //获取相机
      m_MainCamera = Camera.main;
        ...
}

...
private void SetGrassDataUpdate() {
        // 每帧更新的数据
        ...
        if (m_MainCamera != null) {          
          //计算VP矩阵,用于草转换到裁剪空间
          Matrix4x4 v = m_MainCamera.worldToCameraMatrix;
          Matrix4x4 p = m_MainCamera.projectionMatrix;
          Matrix4x4 vp = p * v;
          //传入矩阵
          m_InstantiatedComputeShader.SetMatrix("_VPMatrix", vp);
      }
      else
          m_MainCamera = Camera.main;
}
...
```

***GrassCompute.compute***中加入如下代码

```cpp

...

void Main(uint3 id : SV_DispatchThreadID) {
    ...
    float3 worldPos = mul(_LocalToWorld, float4(sv.positionOS, 1)).xyz;

    //转换到裁剪空间
    float4 absPosCS = abs(mul(_VPMatrix, float4(worldPos, 1)));
    //判断是否在视锥外,放在此处判断的点是C#传进来的点,目前是mesh顶点位置,
    //因此会把整批草都裁掉,也可以放在处理整批草的里面进行处理,
    //但是要注意裁剪后要正确添加到_IndirectArgsBuffer.numVerticesPerInstance里
    if (absPosCS.z > absPosCS.w || absPosCS.y > absPosCS.w * 1.5 ||
        absPosCS.x > absPosCS.w * 1.1) {
      return;
    }
    ...
...
```

***效果***
!(https://cdn.laojiong.site/grassCulling.gif)

## LOD

> 根据相机距离,对草的数量进行裁剪,从而优化数据量。
> 具体实现为,计算当前计算位置点与相机的距离,根据优化的参数计算裁剪比例,剔除部分数量。

***GrassComputeScript***中加入如下代码

```csharp
...
    // LOD
   
    public float minFadeDistance = 40;
    public float maxFadeDistance = 60;
...
...
private void SetGrassDataBase() {
      // 非每帧更新的数据
      ...

      m_InstantiatedComputeShader.SetFloat("_MinFadeDist", minFadeDistance);
      m_InstantiatedComputeShader.SetFloat("_MaxFadeDist", maxFadeDistance);
    }
private void SetGrassDataUpdate() {
        // 每帧更新的数据
      ...

      if (m_MainCamera != null) {
          //传入相机位置
          m_InstantiatedComputeShader.SetVector("_CameraPositionWS",
                                m_MainCamera.transform.position);
            ...
      }
        ...
    }
```

***GrassCompute.compute***中修改如下代码

```cpp
...
    float4 absPosCS = abs(mul(_VPMatrix, float4(worldPos, 1)));

    if (absPosCS.z > absPosCS.w || absPosCS.y > absPosCS.w * 1.5 || absPosCS.x > absPosCS.w * 1.1) {
      return;
    }

    // 根据相机距离计算裁剪比例,用于计算一批次草的数量
    float distanceFromCamera = distance(worldPos, _CameraPositionWS);
    float distanceFade = 1 - saturate((distanceFromCamera - _MinFadeDist) / (_MaxFadeDist - _MinFadeDist));
...
    DrawVertex drawVertices;
    int fadeBladerCount = numBladesPerVertex * distanceFade;
    for (int j = 0; j < fadeBladerCount; ++j) {
        ...
    }
...
    const int addVertexCount = numTrianglesPerBlade * fadeBladerCount;
    for (int i = 0; i < addVertexCount; i++)
    {
      InterlockedAdd(_IndirectArgsBuffer.numVerticesPerInstance,3);
    }
...
```

***效果***
!(https://cdn.laojiong.site/grassLOD.gif)

下一篇,最后一篇将介绍编辑器刷草工具。
页: [1]
查看完整版本: 草(二)Unity移动端可用的草海,Culling&LOD