GPU Gems 2 · Chapter 6. Hardware Occlusion Queries Made Useful
用保守包围盒、空间层次、front-to-back 遍历和上一帧可见性,将硬件 occlusion query 的 latency 与 query overhead 控制在可用范围。
学习目标
- 能解释 hardware occlusion query 为什么同时带来额外 draw call 和结果 latency
- 能用保守包围盒、空间层次和 front-to-back 顺序建立正确的可见性测试
- 能说明 temporal coherence、query queue 与 pull up 如何减少等待和无效 query
- 能判断城市漫游等高遮挡场景适合 occlusion culling,而高空飞越等低遮挡场景应关闭它
硬件 occlusion query 可以询问一段近似几何通过深度测试后产生了多少可见像素。听起来像是“画前先问 GPU”,但朴素使用会产生两个问题:发 query 本身是一条额外绘制命令,读取结果又可能让 CPU 等待 GPU 完成之前的工作。
1. 用保守近似测试复杂物体
↡查询一段近似几何经过深度测试后产生的可见片段数量的硬件机制通常不直接光栅化复杂 mesh,而是关闭颜色写入、保留深度测试,并绘制一个便宜的包围盒。可见像素数大于阈值,就说明复杂物体可能可见;像素为零,才可以跳过复杂绘制。
↡覆盖真实物体屏幕范围、不会把实际可见物体误判为不可见的近似边界是正确性的核心。盒子太松会多做一些工作,但盒子太紧会漏画。靠近相机 near plane 的节点还要注意盒子前面被裁掉导致零片段的问题。
2. 用空间层次减少 query 数量
↡递归把空间划分成内部分组节点和包含实际几何的叶节点的数据结构可以是 k-d tree、BSP、octree 或 bounding-volume hierarchy。内部节点代表一组对象,测试一个被遮挡的内部节点,就可能跳过它下面大量 draw calls、三角形和过度绘制。
朴素的层次算法仍可能为每个可见内部节点发 query,甚至把屏幕填充很多次来测试大盒子。更好的做法需要利用上一帧的分类:上一帧已经可见的内部节点先递归,上一帧不可见的大节点才是值得验证的 query 候选。
3. 用 temporal coherence 隐藏 latency
↡连续帧之间可见性通常变化不大的性质,可用上一帧状态预测当前帧允许系统把上一帧可见当作本帧的工作提示。可见猜错只会多遍历或多画一些,query 结果稍后回来时更新下一帧分类;不可见猜错则必须在结果到达时向下遍历,不能永久跳过。
↡保存已发出但结果尚未可用的 occlusion query,并在遍历期间轮询结果的队列让 CPU 不必在每个节点上停住。遍历 hierarchy 的同时检查队首结果:可用就处理,不可用就继续做前后排序、提交已知可见节点等工作。
4. coherent hierarchical culling 的规则
↡结合空间层次、front-to-back 遍历、上一帧可见性和异步结果队列的层次剔除算法把两条经验组合起来:第一,上一帧可见的 interior node 不发 query,直接访问子节点;第二,上一帧可见的 leaf 可以发 query 但先绘制;第三,上一帧不可见的最大节点发 query,结果可见时再 pull down 遍历其子树。
当 query 结果证明一个节点可见时,visibility 可以向树根 pull up;当一个曾经可见的节点变为不可见,下一帧则不再沿它的路径直接遍历。每帧使用 frame ID 标记访问,避免为整个树做昂贵的全量清零。
5. 什么时候值得用
城市漫游、室内街区和有大块建筑遮挡的动态场景通常适合。它们存在被遮挡的内部节点,一次 query 可以跳过许多对象。高空飞越、开阔平原或几乎没有遮挡的场景则不同:所有节点都可能可见,query 数量和包围盒光栅化反而增加成本。
还可以把它放在 z-only pass 中,先获得稳定的深度和 visibility,再让后续昂贵 shading pass 跳过不可见对象。阈值优化可以把很少像素的对象当成不可见,但阈值过高会误删潜在的 occluder,需要用图像正确性和 profile 一起校验。
6. 动手实验:遮挡、连续性与查询队列
实验中的 query、stall 和 skipped 数量是趋势示意,不是 GPU benchmark。把遮挡调低,观察 query 失去收益;把帧间连续性调低,观察预测变差、等待增多。切换到高空飞越时,应认真考虑关闭 occlusion culling。
Coherent hierarchical culling 实验
用场景遮挡和上一帧连续性观察 query 与等待的变化趋势。
遮挡充分时,层次节点能一次排除多个物体。
小结
- occlusion query 用保守近似几何测试可见像素,但自身有 draw 与 latency 成本。
- spatial hierarchy 把许多对象聚成可一次测试的内部节点。
- temporal coherence 允许先按上一帧可见性做有用工作,再异步纠正猜测。
- query queue、front-to-back 和 pull up 共同减少 stall 与无效 query。
- 高遮挡动态场景适合,开阔场景应通过统计关闭该路径。
练习
问题 1|正确性 一个物体的包围盒 query 返回零像素,但物体实际有一角可见。最可能的两个原因是什么?
问题 2|队列流程 为什么 query 结果未返回时仍能继续渲染上一帧可见的 leaf?
问题 3|场景选择 高空飞越中几乎没有遮挡,是否应该增加更多 query 以“确认可见”?
名词解释
本章出现的专业名词,用大白话再讲一遍。
- occlusion query
- 查询近似几何可见片段数量的硬件机制。
- conservative bounding box
- 覆盖真实物体屏幕范围的保守包围盒。
- spatial hierarchy
- 递归分组空间对象、支持内部节点测试的层次结构。
- temporal coherence
- 连续帧可见性具有稳定性的性质。
- query queue
- 保存未完成 query 并轮询可用结果的队列。
- coherent hierarchical culling
- 利用层次、时间连续性和队列隐藏 query 成本的剔除算法。