在游戏开发中,提升渲染效率最直接的方法就是避免渲染不可见的物体。Unity引擎为此提供了多种渲染剔除(Culling)技术,以优化性能、降低Draw Call和减少Overdraw。
Unity中的三大渲染剔除技术
视锥体剔除(Frustum Culling)
这是引擎自动执行的基础剔除。它利用场景管理算法(2D用四叉树,3D用八叉树)快速收集位于摄像机视锥体(Frustum)内的物体,构成“潜在可见集”。此过程计算开销较大,但不可或缺。
对于需要反射的场景(如镜子、水面),传统方法需要创建额外的反射摄像机并进行同样昂贵的裁剪计算。Unity提供了Reflection Probe组件来离线烘焙静态反射数据,以优化这一过程。
遮挡剔除(Occlusion Culling)
进入视锥体的物体未必最终对玩家可见(例如被一堵墙完全遮挡的雕像)。渲染这些物体是无效的性能开销。遮挡剔除技术通过将场景划分为网格单元,并预先计算每个摄像机可能位置(View Cell)的可见物体集合(Target Cell),在运行时快速查询并仅渲染真正可见的物体,从而显著提升性能。这涉及到复杂的离线烘焙与运行时查询,是性能优化的关键。
摄像机距离剔除(Camera Layer Cull Distance)
此功能允许你为不同的Layer设置摄像机最大可见距离。超出设定距离的该层物体将不被渲染,非常适合用于处理地面装饰物(如小石头、草皮)等细节。
启用方法是为摄像机脚本设置 layerCullDistances 数组:
Camera camera = GetComponent<Camera>();
float[] distances = new float[32];
// 设置名为“Npc”的Layer的剔除距离为11个单位
distances[LayerMask.NameToLayer("Npc")] = 11;
camera.layerCullDistances = distances;
// 启用球面剔除,使得距离判断更符合3D空间逻辑,避免相机转动时物体突兀消失
camera.layerCullSpherical = true;
深度剖析:遮挡剔除(Occlusion Culling)实战
遮挡剔除的核心目标是解决过度绘制(Overdraw)问题,即同一个像素被多次绘制(远处的物体先被绘制,随后被近处物体覆盖)。通过确保只将最终可见的物体送入渲染管线,可以大幅降低GPU负载和Draw Call。
下图展示了未经任何剔除时,所有物体都被渲染的低效情况:

应用视锥体剔除后,大量屏幕外的物体被剔除,渲染负载显著降低:

最后,应用遮挡剔除,仅渲染摄像机实际能看到的物体,这是最理想的高效状态。掌握这类优化手段,是构建高性能应用和云原生/IaaS服务后端逻辑的基础,都体现了对资源(无论是GPU还是CPU/内存)的高效管理思想。

你可以在Scene视图中选择“OverDraw”渲染模式来可视化过度绘制,并在Game视图的Stats面板中查看渲染的三角形总数和批处理次数。
下图未启用遮挡剔除,注意Scene视图中的严重过度绘制(墙后大量房间被渲染),尽管它们在Game视图中完全不可见。

启用遮挡剔除后,远处被遮挡的房间不再渲染,过度绘制大幅减少,绘制的三角形数量和批处理次数也显著下降。

第一步:设置静态物体(Occluder Static / Occludee Static)
要让物体参与遮挡剔除,需要将其标记为“静态”。在Inspector面板的右上角Static下拉框中,勾选 Occluder Static 或 Occludee Static。

- Occluder Static:大型物体,会遮挡其他物体(如墙壁、山脉)。
- Occludee Static:小型物体,通常被其他物体遮挡(如房间内的家具、道具)。
最佳实践:将关卡结构分解为合适的大小。大的遮蔽物(墙壁)应设置为Occluder,而被遮蔽的小物体群(如房间内的多个家具)应分别设置,而不是合并为一个整体,以获取更精细的剔除效果。使用LOD Group时,仅LOD0层级会被视为遮挡物。
第二步:使用遮挡剔除窗口
通过 Window > Rendering > Occlusion Culling 打开窗口。窗口包含三个面板:Object、Bake、Visualization。
在 Object 面板中,可以在场景中选择物体并设置其遮挡类型。

如果选择了遮挡区域(Occlusion Area),则可以在此修改其属性。

重要提示:如果不创建任何Occlusion Area,烘焙将应用于整个场景。务必确保Occlusion Area完全覆盖摄像机所有可能移动到的区域,否则摄像机离开区域后剔除会失效。但过大的区域会增加烘焙时间。
第三步:烘焙(Bake)遮挡数据
切换到 Bake 面板进行参数设置和烘焙。

点击 Set default parameters 可使用适用于大多数场景的默认值。你也可以手动调整以适配特定场景:
| 参数 |
说明 |
| Smallest Occluder |
能够遮挡其他物体的最小尺寸。小于此值的物体将不会遮挡他物。用于平衡剔除精度与数据量。 |
| Smallest Hole |
摄像机视线可以穿透的两个几何体之间的最小“孔洞”直径。 |
| Backface Threshold |
背面剔除阈值。通过忽略摄像机永远不会到达的区域(如地形内部)来优化数据量。值越低,优化越激进。 |
面板底部的 Bake 按钮用于开始生成数据,Clear 用于清除已有数据。烘焙完成后,可以切换到 Visualization 面板预览效果。
第四步:为移动物体创建Occlusion Area
对于动态(非静态)物体,需要创建 Occlusion Area 来定义其活动范围。为一个空GameObject添加 Occlusion Area 组件即可创建。

创建后,在Inspector中调整其 Size 和 Center,并勾选 Is View Volume,以使其能对移动物体进行遮挡剔除。

第五步:测试与调试
烘焙后,在 Occlusion Culling 窗口的 Visualization 面板中,勾选 Occlusion Culling。

在Scene视图中移动摄像机(可在非运行模式下),被剔除的物体会显示为红色(或不可见)。你应该检查是否有物体“突然弹出”(Pop-in),这通常意味着遮挡数据有误或物体设置不当。
烘焙数据以彩色立方体形式可视化:
- 蓝色立方体:代表Target Cells(被遮挡物体的网格划分)。
- 白色立方体:代表View Cells(摄像机位置区域的网格划分)。
如果在测试中发现没有物体被剔除,可能需要将大型物体拆分为更小的部分,以确保它们能被单个Cell完全包含,或者重新审视你的场景划分和参数设置策略,这就像优化算法/数据结构一样,需要找到数据组织与查询效率的最佳平衡点。