MAKE · WRITE · SHARE

Unity-GPUDriven草地渲染

记录一下YCT里GPUDriven 草地渲染实现。

1. 静态数据烘焙

使用地形均匀网格Mesh和美术画的草地Mask,生成草地的分布数据。

原实现以三角形顶点 UV 的 Mask 值作为粗筛,三个顶点通过时保留。这个近似会漏掉内部有草但顶点无草的小区域,也可能忽略内部孔洞;精确分布应在实际候选位置继续采样 Mask。

2. 草地脚本生产GrassInfo

自定义一个GrassInfo的Struct结构,用于上传Compute Shader。

遍历草地分布的三角形,通过插片分布、计算法线、随机旋转的操作,生成具体的GrassInfo数据。

GrassInfo包含草地的transform数据,以及lightmapUV数据。

因为草是依赖地形生成,可以采样地形的ShadowMask,在树和房子下面的草地有阴影效果更好。

草地脚本生产GrassInfo:因为草是依赖地形生成,可以采样地形的ShadowMask,在树和房子下面的草地有阴影效果更好。

草地脚本生产GrassInfo:因为草是依赖地形生成,可以采样地形的ShadowMask,在树和房子下面的草地有阴影效果更好。

3. 草地脚本GPU数据上传

GrassInfo数据在生成结束后,上传到CS StructuredBuffer里。

GPU上存两份GrassInfo的Buffer,一份是所有的_MatrixBuffer,一份是剔除后的_ValidMatrixBuffer。

4. GPU CS剔除

在GPU上进行视锥剔除。

摄像机的Planes和草的AABB进行测试。填充测试通过的草地数据到Buffer。

5. PreZ Pass

可选地先进行深度绘制,尝试减少后续着色 Overdraw。风变形、Alpha 覆盖与 LOD 必须在深度和主 Pass 一致;多一次几何与 Mask 工作是否值得,需要在目标平台测量。

使用DrawIndirect。

6. GPU DrawIndirect

GPU直接绘制草地。

1
cmd.DrawMeshInstancedIndirect(GrassUtil.unitMesh,0,grassTerrian.material,0,grassTerrian.argsBuffer);

7. 均匀采样与间接参数

地形网格均匀只是一种方便条件,不是均匀草密度的充分条件。三角形应按面积分配样本数,在三角形内使用均匀重心采样,例如令 r=sqrt(u),权重取 (1-r, r*(1-v), r*v)。直接对三个权重独立随机再归一,通常不均匀。

每帧清零可见计数,保证输出容量,并把可见数写到正确的 Indirect 参数字段。CPU/GPU 的 GrassInfo 布局与 stride 一致;风动画需要扩展包围体,否则静态 AABB 会错误剔除。

地形 Shadowmask 的采样必须使用正确图集 UV、通道和 Mixed Light 约定,不能把遮罩当作通用阴影颜色直接乘上。多相机与阴影视图也应定义独立可见性范围。

总结

草地链路包括分布、实例数据、剔除、计数与间接绘制。GPU Driven 减少 CPU 逐实例提交,但数据生成是否均匀、Buffer 是否有效和深度路径是否一致,仍决定画面与性能。