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 完成之前的工作。

Occlusion query:用便宜的包围盒回答“会不会有像素”1Issue开始 query2Bounds绘制保守包围3Result读取可见像素4Render可见才画复杂物体包围盒必须覆盖原物体的屏幕范围,否则会产生错误的“不可见”
query 只测试便宜且保守的近似几何;真正的复杂物体只在结果允许时绘制。

1. 用保守近似测试复杂物体

通常不直接光栅化复杂 mesh,而是关闭颜色写入、保留深度测试,并绘制一个便宜的包围盒。可见像素数大于阈值,就说明复杂物体可能可见;像素为零,才可以跳过复杂绘制。

是正确性的核心。盒子太松会多做一些工作,但盒子太紧会漏画。靠近相机 near plane 的节点还要注意盒子前面被裁掉导致零片段的问题。

2. 用空间层次减少 query 数量

可以是 k-d tree、BSP、octree 或 bounding-volume hierarchy。内部节点代表一组对象,测试一个被遮挡的内部节点,就可能跳过它下面大量 draw calls、三角形和过度绘制。

Spatial hierarchy:让一次 query 覆盖一组对象rootvisible nodeoccluded nodeleafleafleafleafleafleaffront-to-back 访问让近处可见物体先填充深度,远处节点更容易被排除
在层次结构中测试大的 occluded 内部节点,一次 query 可以跳过它下面的许多 draw calls。

朴素的层次算法仍可能为每个可见内部节点发 query,甚至把屏幕填充很多次来测试大盒子。更好的做法需要利用上一帧的分类:上一帧已经可见的内部节点先递归,上一帧不可见的大节点才是值得验证的 query 候选。

3. 用 temporal coherence 隐藏 latency

允许系统把上一帧可见当作本帧的工作提示。可见猜错只会多遍历或多画一些,query 结果稍后回来时更新下一帧分类;不可见猜错则必须在结果到达时向下遍历,不能永久跳过。

让 CPU 不必在每个节点上停住。遍历 hierarchy 的同时检查队首结果:可用就处理,不可用就继续做前后排序、提交已知可见节点等工作。

Temporal coherence:用上一帧分类填充 query latencyFrame Nnode visible → render nowFrame N + 1issue query → queueresult → correct可见猜测:先做有用工作;结果回来后若变不可见,只更新下一帧状态关键:不能把“上一帧不可见”直接当作本帧最终答案
预测错为可见只会多做工作;预测错为不可见可能漏画,因此不可见猜测必须在结果到达时拉回遍历。

4. coherent hierarchical culling 的规则

把两条经验组合起来:第一,上一帧可见的 interior node 不发 query,直接访问子节点;第二,上一帧可见的 leaf 可以发 query 但先绘制;第三,上一帧不可见的最大节点发 query,结果可见时再 pull down 遍历其子树。

Query policy:该测的测,该跳的跳上一帧可见 interior:直接遍历,不发 query上一帧可见 leaf:可 query,同时先绘制上一帧不可见的最大区域:发 query 验证结果可用:pull up / pull down 更新层次状态query 数量接近可见对象规模,而不是总对象数或树深度
pull up 用子节点结果推导父节点可见性,避免为已经知道可见的内部节点反复发 query。

当 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 与等待的变化趋势。

城市街区:有大块遮挡occluder可跳过节点:39 · queries:5 · stall:5queue 让 GPU 工作与结果等待交错趋势示意,不替代真实 GPU profile

遮挡充分时,层次节点能一次排除多个物体。

小结

  • 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 成本的剔除算法。

资料与写作方式声明

本章以GPU Gems 2 · Chapter 6. Hardware Occlusion Queries Made Useful权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…