GPU Gems 2 · Chapter 7. Adaptive Tessellation of Subdivision Surfaces with Displacement Mapping

把 Catmull-Clark subdivision surface 分成 patch,在 GPU 纹理缓冲中重复执行 flatness test、细分和 limit 属性计算,并把 displacement 计入屏幕误差。

学习目标

  • 能解释 Catmull-Clark subdivision surface 如何从低密度控制网格得到平滑曲面
  • 能用屏幕像素 flatness 判断 patch 继续细分还是直接计算 limit position 和 normal
  • 能描述 patch texture、GPU shader 循环与 CPU 索引管理之间的边界
  • 能把 displacement 产生的额外误差加入细分决策,并说明如何避免相邻 patch 裂缝

Subdivision surface 用少量控制点描述平滑曲面,适合动画角色和需要高质量几何的模型。但实时渲染不能盲目把整个控制网格预先细分到最高级别:相机距离和曲率决定了屏幕上是否真的需要这些顶点。

Control mesh → Catmull-Clark subdivision → smooth patchcontrol mesh少量控制点subdivided patch规则网格 + 平滑曲面
控制网格是紧凑的输入;重复 subdivision 让曲面更密、更接近 limit surface。

1. Catmull-Clark 与 patch

每次将四边形面拆成四个面,并按 face、edge、vertex 的权重更新位置。不断细分的理论结果是 ;实际渲染只需在误差足够小时逼近它。

通常使用全 quad 拓扑。为了让 GPU 以规则二维数组处理任意模型,预处理阶段将控制网格分成 patch,并为每个 patch 保留邻接 ring、尺寸和 extraordinary corner 的 valence 信息。

2. 用屏幕 flatness 做 adaptive tessellation

固定细分的两个坏结果是 over-tessellation 和 under-tessellation。更稳健的判据是把曲线或 patch 到 limit surface 的最大距离换算成屏幕像素:误差小于阈值就输出,误差大于阈值就继续细分。近处和弯曲处自然获得更多几何。

可对 patch 的每条边进行,先测试 control points 形成的 B-spline 曲线,再把透视深度纳入缩放。近裁剪面附近要特别处理 z 值,避免不必要的过度细分。

Flatness test:误差进入屏幕像素空间flat / far误差低于阈值 → stopcurved / near误差高于阈值 → subdivideflatness 以屏幕像素衡量,避免远处过密、近处不足
近处或弯曲处继续细分,远处或平坦处停止;相同模型不必处处使用相同细分级别。

3. 在 GPU 纹理缓冲中循环

把顶点数据放到 GPU 可读取的纹理中。shader 先将控制点拷贝到 patch buffer,flatness shader 写出边缘误差;平的 patch 计算最终属性,不平的 patch 渲染到另一张 buffer,再交换 source 和 destination。

GPU tessellation:patch buffer 之间反复筛选与细分Patch texture控制点ringFlatness边缘误差Subdivideshader 生成下一层Limit attrs位置normal不平的 patch 写入下一层;平的 patch 直接计算最终属性
patch buffer 在纹理间 ping-pong;CPU 只管理 buffer 和索引,不触碰每个顶点数据。

CPU 仍然负责 buffer 分配、读取压缩后的 flatness 结果和构建 primitive index list,但不需要逐顶点搬运每次 subdivision 产生的数据。limit shader 计算位置,normal shader 计算切线叉积;二者可以通过多个 render target 一起写出。

4. displacement 如何进入几何预算

与只改变光照的 bump 或 normal mapping 不同。要避免 displacement 区域细分不足,预处理或 shader 计算每条 patch edge 两侧的最大 displacement,并把它加到 flatness error 中。

最终的 limit position 沿法线加入 displacement;normal mapping 仍可用来处理逐像素细节,并减少 adaptive tessellation 改变时的 shading popping。几何轮廓由 displacement 负责,光照细节由 normal map 补充,二者职责不能互换。

5. watertight tessellation

Watertightness:adaptive depth 变化也不能露缝patch A:深patch B:浅共享边 / T-junction → 复用远边索引补三角形不要依赖相邻 shader 浮点计算恰好一致
自适应深度不同并不等于允许出现缝;索引构建阶段要统一共享边和 T-junction。

相邻 patch 的运算顺序可能不同,不能赌浮点结果逐位相同。索引生成器应统一使用一侧 patch 的共享边和 corner 数据。若一个 patch 已经细分、邻居仍平坦,就出现 T-junction;可以把 junction 顶点替换为更远的边界顶点并补一组三角形,保持无缝连接。

6. 动手实验:误差预算随视图变化

先把距离调远,观察级别下降;再增加曲率和 displacement,观察相同距离下细分重新增加。实验数值是决策趋势的可视化,不是具体 GPU 时间测量。

Adaptive tessellation 实验

在屏幕误差阈值下,近处、弯曲处和 displacement 区域需要更多细分。

patch preview · subdivision level 6屏幕距离 46% · 曲率 60% · displacement 28%

当前级别 6。displacement 会加入 flatness error,不能只按原始曲率决定细分。

小结

  • control mesh 经过 Catmull-Clark subdivision 逼近 limit surface。
  • flatness test 以屏幕像素误差决定 patch 是否继续细分。
  • patch buffer 让 GPU 在纹理中循环生成细分结果,CPU 主要管理索引与内存。
  • displacement 必须进入几何误差和 limit position,而不仅是法线贴图。
  • watertight 索引和 T-junction 处理保证不同 adaptive depth 仍无裂缝。

练习

问题 1|停止条件 为什么一个远处但很弯的 patch 仍可能需要细分,而一个近处平面 patch 可以停止?

问题 2|职责分配 GPU 细分算法中,为什么 CPU 可以读取 flatness 结果和构建索引,却不必读取每个新顶点?

问题 3|displacement 如果只在最后用 normal map 表现高频 displacement,会丢失哪两类视觉信息?

名词解释

本章出现的专业名词,用大白话再讲一遍。

Catmull-Clark subdivision surface
通过递归面、边、顶点规则生成平滑曲面的表示。
limit surface
无限次 subdivision 后的理论平滑曲面。
control mesh
用于生成 subdivision surface 的低密度输入网格。
flatness test
以屏幕像素误差决定 patch 是否继续细分的测试。
patch buffer
以二维纹理保存 patch 控制点和中间细分结果的布局。
displacement mapping
沿曲面法线移动几何顶点、改变轮廓和遮挡的纹理细节。

讨论

评论区加载中…