性能优化(20)——渲染提前期优化——Culling、Simplization、Batching

  1. 1. Culling剔除

  • Culling(剔除)、Simplization(简化)、Batching(合批)
  • 这些优化都是为了缩短渲染提前期,是为了进GPU管线渲染前所做的优化。将这三个过程类比桃子食品加工生产销售前的过程:将Culling剔除理解为摘桃子阶段,去除不适合的树枝和叶子、腐烂的桃子等,因为它们不适合生产;Simplization理解为为桃子分类,将不同的桃子按不同品质大小分类装箱,为了生产不同的桃子产品,或满足不同的销售渠道;Batching合批可以理解为将桃子装载发货的过程,近距离的放普通的货车,有保险需求的上冷藏车,大量需求的上火车,时间需求的上飞机,有的地方一天一发货要怎么安排运力,有的地方一周一发货要怎么安排运力,总之要保证下游能源源不断地得到桃子供应,而不会中断生产加工或销售的流程。而这些过程一般都发生在生产之前,类比于进入GPU管线之前,通常情况下这些工作都由CPU端负责,但一些特殊的生产流程可以利用生产流水线的特殊功能对桃子进行分类、清洗、剔除残次品,这个过程可以理解为做了GPU加速,将本该属于CPU端的工作放到了GPU流水线中进行了。其实总体而言,Culling剔除、Simplization简化、Batching合批三者在实际应用中并不孤立,相反是要紧密结合使用的,它们对渲染前的优化是非常重要且必要的

Culling剔除

  • 哪些是需要剔除的内容

  • 广义上讲:

    • 看不见的像素、网格和对象
    • 重复的、用不到的资源
    • 不需要、不执行的代码
  • 其中,剔除看不见的像素、网格和对象的方法:

    • 像素剔除:摄像机平截头剔除、Back-face Culling(背面剔除)、Early-Z、Pre-Z Pass。其中摄像机剔除、背面剔除与Early-Z都是渲染库或硬件直接支持的部分,而Pre-Z Pass是针对于前向渲染下Early-Z失效的情况下,通过Pre-Z Pass方式提前获取场景深度,后续绘制像素时,根据场景深度外壳进行像素着色计算的剔除。是Unity2021URP下直接提供的新功能

    • 网格对象级别的剔除:Unity提供了Layer Mask、可视距离对象剔除与Occlusion Culling的方案,前两种都可以通过简单的设置完成,而Occlusion Culling是一种CPU+烘焙的方案,在某些OverDraw严重而又存在大面积建筑或遮挡体类型的游戏中,可以起到加速的效果,但Occlusion Culling本身是一把双刃剑,由于烘焙会有额外的内存开销,而所有关于遮挡剔除的计算又在CPU端,会由额外的CPU开销。如果剔除给GPU端的优化弥补不了CPU端的开销时,这种方案可能是一种负优化,需要测试使用
    • 灯光剔除:这部分需要依赖特殊的图形库和硬件的架构完成,比如Tile-Based Deferred Rendering,也就是通常所说的TBDR管线和Forward+渲染管线,针对于多实时光源在游戏项目上的光源剔除优化,在灯光处理上会为每个Tile建立可用的灯光列表,这时不能影响该Tile内像素的灯光将会被剔除在列表之外,这样在计算该Tile中的像素着色时可以大大节省像素光照着色的开销。目前URP下的Tile-Based延迟渲染在Unity2021中已经支持。URP Forward+管线目前仍在实验阶段,但仍可在Unity2021URP下通过定义URP_ENABLE_CLUSTERED_UI的宏来开启,从宏定义中我们也可以看出URPForward+采用的加速结构是基于Clustered的。对比而言,即使Tile-Based延迟渲染在带宽与内存上做了优化,但仍会比Forward+要高,而Forward+的性能瓶颈依然在场景对象复杂度上。因此如何选择管线要根据自己的游戏类型和目标设备来进行选择
    • 场景剔除:是针对于大场景、多场景拼接的地图,这时我们可以通过Unity Additive的场景根据逻辑来做异步的加载和卸载,以实现场景的动态剔除,这也算是另类的Culling的一种了

  • 除Unity提供的一些Culling的优化方案外,用户也可以扩展自己的Culling优化方案。如默认Unity下没有场景数据结构管理,可以通过添加各种场景结构来对场景中的对象进行管理,如Octree、BSP Tree、Portal等,还有对整个场景进行体素化(Voxelization),或者计算场景的SDF,这些数据结构都是为了做Culling加速或其他功能的必要数据。另外还有一些利用GPU加速的算法,如通过Hi-Z Pass利用上一帧的深度图和摄像机矩阵;利用Temporal Reprojection Culling算法来对当前这个场景做剔除;另外还有Cluster、Tile-based Visible Buffer算法,这些都可以在Unity的管线中进行扩展集成。

  • 一般而言,基于GPU的Culling方案在移动端都会面临兼容性的问题;而基于CPU的方案往往都会存在可能变成负优化的双刃剑。其实没有任何一种Culling方案能适用于所有游戏项目,如果想扩展自己的Culling方案,往往需要代码实现的功能能力要大于理解算法本身的能力,所以不是优化到极致或者超大规模商业项目,一般很少有团队会去尝试