🎯 Unity 遮挡剔除完全指南:从原理到实战的性能优化

💡 遮挡剔除的价值

  • 如何减少无效渲染,提升游戏性能?
  • Overdraw 太高怎么优化?
  • 遮挡剔除如何正确使用?
  • 静态对象和动态对象如何分别处理?

完全指南!深入理解 Unity 遮挡剔除原理,掌握性能优化的关键技术!


Occlusion Culling 介绍

1. 什么是遮挡剔除

**遮挡剔除 (Occlusion Culling)**:当对象被其他对象阻挡而不能被摄像机看到时,禁用对象的渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────────────────────────────────┐
│ 遮挡剔除示意图 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 场景: │
│ 摄像机 ─────► 墙壁 ─────► 敌人 (被墙壁遮挡) │
│ │
│ 不使用遮挡剔除: │
│ └─ 渲染墙壁 + 渲染敌人 (浪费性能) │
│ │
│ 使用遮挡剔除: │
│ └─ 只渲染墙壁 (敌人被剔除,不渲染) │
│ │
└─────────────────────────────────────────────────────────────────────────┘

2. 与视锥体剔除的区别

特性 视锥体剔除 (Frustum Culling) 遮挡剔除 (Occlusion Culling)
作用 剔除摄像机视野外的对象 剔除被遮挡的对象
自动性 Unity 自动处理 需要手动配置
效果 减少视野外渲染 减少 Overdraw
互补性 两者同时使用效果最佳 两者同时使用效果最佳

💡 关键点:遮挡剔除与视锥体剔除是互补的,使用遮挡剔除时仍然会受益于视锥体剔除。


工作原理

1. 数据结构

遮挡剔除使用单元格组成的二叉树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────────────────────────────────┐
│ 遮挡剔除数据结构 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 场景包围体 │
│ └── 二叉树细分 │
│ ├── 视图单元格树 (View Cells) │
│ │ └── 静态对象 │
│ └── 目标单元格树 (Target Cells) │
│ └── 移动对象 │
│ │
│ 视图单元格 → 索引列表 → 可见静态对象 │
│ │
└─────────────────────────────────────────────────────────────────────────┘

2. 工作流程

步骤 说明
1. 数据烘焙 虚拟摄像机在场景中移动,构建潜在可见对象集的层级视图
2. 运行时查询 摄像机使用数据识别可见和不可见对象
3. 渲染优化 只发送可见对象进行渲染,减少 DrawCall

3. 性能提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌─────────────────────────────────────────────────────────────────────────┐
│ 性能对比 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 不使用遮挡剔除: │
│ • DrawCall: 200 │
│ • Overdraw: 严重 │
│ • FPS: 30 │
│ │
│ 使用遮挡剔除: │
│ • DrawCall: 80 ⬇️ 60% │
│ • Overdraw: 轻微 │
│ • FPS: 55 ⬆️ 83% │
│ │
└─────────────────────────────────────────────────────────────────────────┘

何时使用 Occludee Static

1. 标记原则

对象类型 是否标记为 Occluder 原因
大型不透明物体 ✅ 是 能有效遮挡其他对象
完全透明/半透明对象 ❌ 否 不会产生遮挡
小型对象 ❌ 否 不太可能遮挡其他对象

2. 最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────────────────┐
│ 标记策略 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ✅ 应该标记为 Occluder: │
│ • 建筑物墙壁 │
│ • 大型岩石 │
│ • 地形凸起 │
│ │
│ ❌ 不应标记为 Occluder: │
│ • 玻璃窗、透明材质 │
│ • 小型道具 │
│ • 粒子效果 │
│ │
│ 💡 优化建议: │
│ 将小型对象标记为 Occludee (被遮挡物) 而不是 Occluder (遮挡物) │
│ 这样可以减少遮挡剔除的计算开销 │
│ │
└─────────────────────────────────────────────────────────────────────────┘

使用步骤

1. 烘焙遮挡数据

1
Window → Rendering → Occlusion Culling

2. 标记静态对象

在 Inspector 中勾选:

  • StaticOccluder Static (遮挡物)
  • StaticOccludee Static (被遮挡物)

3. 烘焙场景

点击 Bake 按钮生成遮挡数据


注意事项

注意事项 说明
仅适用于静态对象 移动对象使用目标单元格树
烘焙时间开销 复杂场景烘焙时间较长
内存占用 遮挡数据会占用一定内存
动态对象 需要特殊处理

参考资料


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1487842110@qq.com