Culling
Culling 跳过相机看不见的内容——不绘制视锥外或被遮挡物完全挡住的对象,从而减少 Unity 的 draw call 与 GPU 压力。Frustum Culling 与 Occlusion Culling 是优化大场景帧率的关键手段,帮助 TA 与程序在开放世界、城市关卡中有效控制渲染成本。
想象一下...
Culling 是聪明的舞台管理员。不会给台上每个演员都开灯,只给观众席能看见的开灯。在幕后?关灯。不在视角内?关灯。GPU 根本不会知道它们——每帧节省成千上万次 draw call。
概念详解
Frustum Culling 是基础——Unity 用相机视锥的六个平面 (由 FOV 与 near/far clip 形成的截锥)去测每个 Renderer 的包围盒。若包围盒完全在外 → 跳过 draw call。内置、始终开启、无需配置。
Occlusion Culling 更进一步——跳过在视锥内但被完全挡住的对象。 Unity 用 Umbra 预计算遮挡数据(bake):Window → Rendering → Occlusion Culling → Bake。 在 Static 下拉中标记 occluder 与 occludee。室内场景很强,开放世界较弱。
LOD(Level of Detail) 相关——不是完全剔除,而是随距离降低 网格复杂度。LOD0 = 完整细节(近),LOD1 = 中等,LOD2 = 简化,LOD3 = 广告牌,Culled = 隐藏。 是按距离的画质/性能权衡。
Backface Culling 在 GPU 层——法线背向相机的三角形会被丢弃。
每个 shader 默认开启,封闭网格可省约 50% 片元开销。Material 上选 “Two Sided” 或 ShaderLab 中
Cull Off 可关闭。
剔除类型
Frustum Culling
摄像机视野外的对象会被完全跳过。 自动,无需设置。对所有项目都有效。
Camera.layerCullDistances[]
Occlusion Culling
被墙/建筑挡住的对象会被跳过。 需要 bake 并标记 Static。最适合室内场景。
Static → Occluder/Occludee
LOD Group
对象远离摄像机时降低多边形数量。LOD0→LOD3→Culled。 需要准备 多套 mesh。适合开放世界。
LODGroup component
动手步骤
Frustum Culling:收紧 Camera Far Clip
Camera → Far Clip Plane:从 1000 降到 200–500,剔除远处物体。用雾掩盖切面。
为 Occlusion Culling 标记 Static
选中墙/地板/天花板 → Inspector → Static → Occluder Static 与 Occludee Static。
Bake 遮挡数据
Window → Rendering → Occlusion Culling → Bake。添加 Occlusion Area 控制 bake 体积。
添加 LOD Group
Add Component → LOD Group → 把 LOD0/1/2 网格拖进各槽,调节屏幕占比 %。
用 Rendering Statistics 测量
Game View → Stats:移动相机时观察 "Batches" 与 "Tris"。Scene 视图可开 Occlusion Culling Visualization。
交互模拟器
绕场景旋转相机,观察哪些对象被视锥剔除与遮挡剔除。
Visible: 0
Culled: 0
代码示例
基础设置按层剔除距离,并检查 renderer 是否可见。
using UnityEngine;
public class CullingSetup : MonoBehaviour
{
void Start()
{
Camera cam = Camera.main;
// Per-layer cull distance
// 32 entries (32 layers) — 0 = use Far Clip
float[] distances = new float[32];
distances[8] = 50f; // Layer 8 "SmallProps": cull after 50m
distances[9] = 150f; // Layer 9 "MediumProps": cull after 150m
distances[10] = 0f; // Layer 10: use Camera.farClipPlane
cam.layerCullDistances = distances;
// Spherical culling: from camera center, not frustum planes
cam.layerCullSpherical = true;
}
// Is this renderer currently visible to a camera?
void CheckVisibility(Renderer rend)
{
if (rend.isVisible)
Debug.Log(rend.name + " is being rendered");
}
}代码示例
进阶用代码配置 LOD Group,并强制指定 LOD 级别(调试 / 过场)。
using UnityEngine;
public class DynamicLOD : MonoBehaviour
{
public Renderer[] lod0, lod1, lod2;
LODGroup group;
void Awake()
{
group = GetComponent<LODGroup>();
if (group == null)
group = gameObject.AddComponent<LODGroup>();
// Configure LOD levels from code
LOD[] lods = new LOD[3];
lods[0] = new LOD(0.5f, lod0); // 50%+ screen → LOD0
lods[1] = new LOD(0.15f, lod1); // 15-50% → LOD1
lods[2] = new LOD(0.03f, lod2); // <3% → LOD2, <0 → culled
group.SetLODs(lods);
group.RecalculateBounds();
}
// Force a specific LOD (debug or cutscene close-up)
public void ForceLOD(int level)
{
group.ForceLOD(level); // -1 = disable force, use automatic
}
}📌 快速记忆
- ▸Frustum Culling:自动,无需配置
- ▸Occlusion Culling:需要 bake,室内最佳
- ▸LOD Group:随距离降低面数
- ▸layerCullDistances:按层设置剔除距离
- ▸renderer.isVisible:正在被渲染时为 true
⚠️ 常见错误
❌ Occlusion 已 bake 但什么都不剔除
忘记 Camera → Occlusion Culling = true
✅ Camera Inspector → Rendering → 勾选 Occlusion Culling
❌ LOD 跳变(缩放时网格突然切换)
LOD 过渡跨度太大,未开 LOD Fade
✅ 在 LOD Group 上设 "Fade Mode = Cross Fade" 做混合