Unity静态批处理,动态批处理,GPU Instancing
0.参考
Unity性能优化基础篇——静态批处理,动态批处理,GPU Instancing - 知乎 (zhihu.com)
1.静态批处理
Unity的静态批处理可以提高场景中大量重复物体的渲染性能。它的原理如下:
- 将需要进行批处理的物体按照材质和网格分组。
- 对于每个网格,计算出其在世界坐标系下的变换矩阵。
- 将相同材质的物体的顶点数据合并成一个大的顶点缓冲区,并为每个物体创建一个索引缓冲区,以便可以快速地绘制它们。
- 在渲染时,将各个网格的变换矩阵传递给着色器,使用GPU实现一次性绘制所有物体。
由于静态批处理可以减少CPU与GPU之间的通信,因此它可以显著提高渲染性能,特别是在有大量重复物体的场景中。需要注意的是,静态批处理只对静态物体生效,对于动态物体需要使用其他优化技术。
静态批处理的主要限制是每批可以具有的顶点和索引的数量,通常为每个64k。
并且静态批处理的缺点是增加了内存使用量。如果您有100个石头,每个石头模型占用1MB,则内存使用量将超过100MB。因为批处理将所有石头一起包含在一个网格中。
2.GPU Instancing
Unity GPU Instancing可以通过批量渲染相同网格、相同材质的物体来提高渲染性能。它的原理如下:
- 首先,将需要进行GPU Instancing的物体按照网格和材质进行分类。
- 对于每个网格和材质组合,Unity会创建一个GPU Instance Buffer,并将所有物体的变换信息存储在这个缓冲区中。每个实例对应着一个变换矩阵。
- 在渲染时,Unity会将这些变换矩阵传递给着色器,并使用GPU实现一次性绘制所有物体。同时,着色器可以根据每个实例的变换矩阵来确定其在世界坐标系下的位置、旋转、缩放等信息,从而实现批量渲染。
由于GPU Instancing可以减少CPU与GPU之间的通信,从而降低了CPU的负担,因此可以提高渲染性能,特别是在需要大量重复物体的场景中。
需要注意的是,对于动态物体而言,GPU Instancing的原理与静态物体基本相同。不同之处在于,变换信息需要每帧更新,因为动态物体的位置、旋转和缩放等属性会发生变化。为了支持动态物体的GPU Instancing,Unity提供了SetMatrixArray函数,可以在每帧中更新实例的变换矩阵数据,实现动态批处理。
GPU Instancing对动态物体的性能提升效果可能不如对静态物体,因为动态物体需要每帧更新变换矩阵数据,会增加CPU的负担。同时,实现GPU Instancing对动态物体也需要一些额外的代码开销。因此,在使用GPU Instancing时,需要根据具体情况进行选择和使用。
GPU Instancing一般在以下场景中使用:
- 大量重复的静态物体:当场景中有大量相同网格、相同材质的静态物体时,可以使用GPU Instancing批量渲染它们,从而提高渲染性能。
- 动态物体数量较少,但需要频繁更新变换信息:虽然GPU Instancing对动态物体的性能提升效果不如对静态物体,但是当动态物体数量较少且需要频繁更新变换信息时,使用GPU Instancing仍然可以提高渲染性能。
- 需要在移动设备上实现高质量渲染:由于移动设备的GPU通常性能较低,因此需要尽可能地减少CPU与GPU之间的通信以提高性能。在这种情况下,GPU Instancing可以发挥很好的优化效果。
总而言之,GPU Instancing适用于需要大量渲染相同网格、相同材质物体的场景,并且可以减少CPU与GPU之间的通信成本以提高性能。
2.动态批处理
Unity的动态批处理可以提高场景中大量动态物体的渲染性能。它的原理如下:
- 首先,Unity将所有需要进行批处理的物体按照材质和网格分组。
- 对于每个网格,Unity会计算出其在世界坐标系下的变换矩阵。
- 对于每个材质,Unity会将其所有物体的顶点数据复制到一个共享的顶点缓冲区中,并为每个物体创建一个对应的索引缓冲区。
- 在渲染时,Unity会将每个物体的变换矩阵传递给着色器,使用GPU实现一次性绘制所有物体。
- 当有物体发生变化时,Unity会标记这些物体为“dirty”,并在下一帧重新生成它们的顶点数据和索引缓冲区,以保证正确性。
由于动态批处理只对发生变化的物体重新生成数据,因此可以减少重新计算和上传数据的成本,从而提高渲染性能。但需要注意的是,如果有大量动态物体同时发生变化,需要谨慎使用动态批处理,否则可能会出现性能瓶颈。
动态批处理受到更加严格的限制。你只能将其应用于具有少于300个顶点和900个顶点属性(颜色,UV等)的网格。材质也应使用single-pass着色器。
不仅如此,动态批处理非常不可预测。你无法真正确定对象将如何被批处理。结果通常会随着帧的变化而变化。
反正不到万不得已,最好不要使用
4.Unity运行时批处理API
最后是我们通过代码来运行时做批处理操作
Unity有2个强大的API,可以在运行时合并网格。
1,最简单的方法是使用StaticBatchingUtility.Combine。该函数传入一个根游戏对象,并将遍历其所有子对象并将其几何形状合并为一个大块。但是有个限制是,要批处理的所有子网格,导入设置必须允许开启CPU read/write
所有物体都在这个节点下StaticBatchingUtility.Combine(BatchedTanks.gameObject);,这样就自动合批了
2,第二种方法是使用Mesh.CombineMeshes。此函数间接获取网格列表并创建组合的网格。然后,你可以将该网格分配给mesh filter渲染,这种合并方式比较复杂,可以分好几次来讲,推荐大家看一篇文章,讲的比较详细
【Unity3D】 合并mesh那些事 CombineMeshesblog.csdn.net/tw_345/article/details/79771454