GPU Gems 3 · Chapter 34. Signed Distance Fields Using Single-Pass GPU Scan Conversion of Tetrahedra

从有符号距离场的零等值面出发,解释 T4 窄带 shell、z 平面切片、closest-feature 距离和伪法线符号如何组成一次 GPU 扫描。

学习目标

  • 能从零等值面、正负号和最近距离三个角度解释 signed distance field,并说明为什么高分辨率网格会让 CPU 预处理成为瓶颈
  • 能沿着 T4 GPU scan 的顺序复述 triangle → tetra shell → z-slice → fragment program → depth-min 的数据流,并指出 single-pass 的真实边界
  • 能修改 SDF Lab 的 mesh 拓扑、网格分辨率、窄带宽度、查询目标和执行模式,判断伪法线能修复什么、fold 为什么仍需要额外处理

先问:碰撞查询为什么需要一张距离地图

上一章把碰撞候选 pair 交给了几何求解器。本章换一个视角:如果每个空间点都能快速回答“离物体表面还有多远”,碰撞、射线和路径规划就不必每次都从三角形集合重新开始。问题是,直接在 CPU 上对高分辨率体网格逐点查最近三角形,代价会随着网格尺寸迅速累积。

1. SDF 把几何表面变成可查询的标量场

一个 SDF 不是“每个三角形的距离列表”,而是空间网格上的标量采样。对点 p,绝对值回答“离表面多远”,符号回答“在哪一侧”;这两个信息一起才足以支持穿透判断和连续的距离查询。

an SDF turns a surface into a queryable distance mapzero isosurfaceinside · d < 0outside · d > 0voxel sampleone stored valued(p)sign + magnitudecollision queryray / path queryinterpolate between voxelsthe grid is the index; the zero crossing is the geometric surface

高分辨率网格的体素数量是 。因此本章不追求对整个空间都做昂贵计算,而是围绕表面生成一个用户指定的窄带;远离表面的区域若不参与当前应用,就不必进入扫描。

2. 用统一的 tetra shell 覆盖窄带

实现选择了一个简单的统一几何单元:每个三角形先用最长边和正交高度拟合矩形,再按窄带宽度扩大,并沿面法线向内外挤出形成 OBB;OBB 再分解成五个四面体。这个构造可能产生重叠和冗余,但它容易验证,能覆盖所需窄带。

make the narrow band simple enough to slice1 · trianglenormal nlongest edge + height2 · shellOBB → 5 tetrahedracover Voronoi regions3 · z slicefragment programsigned distancethe CPU builds coverage; the GPU evaluates each covered voxel slice

这个顺序还有一个 GPU 友好的性质:四面体被 z 平面切开时,截面只有有限的拓扑情况,而且截面多边形是凸的。于是可以把 CPU 的工作压缩为“找当前 z 切片相交的 tetrahedra”,把每个截面交给光栅化和 fragment program。

3. 每个 fragment 先找最近 feature,再决定符号

一个 voxel 落进 tetra shell,并不意味着它离三角形的面最近。它可能更接近一条边或一个顶点;如果一律用 face plane 的距离,窄带边界和尖锐折角附近就会出现错误的符号。

fragment program 将截面坐标变换到三角形的局部 frame,在有限个区域中判断最近的是 face、edge 还是 vertex。随后计算 |p − c|,其中 c 是最近 feature 上的点;距离的绝对值不是最后答案,符号还要由同一个 feature 的方向信息提供。

distance is the closest feature, not always the face planep₀ → edgep₁ → vertexp₂ → facecase analysis picks one cclosest feature c|p − c|pseudonormal n(c)d = n(c) · (p − c)sign and magnitudeone fragment, one answerangle-weighted pseudonormals carry local surface orientation through vertices and edges

对最近 feature 的点 c 和伪法线 n(c),符号可以由 d = n(c) · (p − c) 的正负判断。这样,面、边和顶点都能沿着与其局部几何一致的方向给出 inside/outside,而不是让一个邻近平面替它做决定。

4. 先理解 leaking,再理解 single-pass 的边界

the sign must follow the feature, not just its planeplane test · leakinside voxels look positiveneighboring face owns the signpseudonormal · repairn(c)sign from closest featurefolds still need another passsingle-pass removes plane-test leaks, but it does not make folded topology unambiguous

本章的 single-pass 主要指:在一个 z-slice 的片段阶段,同时计算距离和符号,不需要先做一轮无符号距离、再做第二轮全局 sign flood。它不等于“没有 z-slice 循环”,也不等于“没有 readback”:原始实现会为每个 z 增量渲染相交截面,读取当前切片,再移动到下一层。

动手走一遍:T4 GPU scan 如何从 shell 走到 SDF

分步1 / 4

先建立窄带 shell

CPU 遍历三角形,按窄带宽度生成 OBB,并把每个 OBB 分解成五个 tetrahedra;重叠是可接受的,首要目标是完整覆盖 Voronoi 区域。

let depth choose the nearest candidate for usfragment candidated = −0.42same voxel, one candidatetwo bufferscolor: signed d−0.42depth: |d|0.42depth test = min |d|voxel result−0.42signed winnerread back per z slicestore in SDF gridthe “single pass” is per slice: depth resolves overlaps while the CPU advances the z sweep
one z-sweep, four teaching beats1 · shellOBB → tetrahedraCPU coverage2 · slicez-plane sweepcross-section polygon3 · fragmentd(p)closest featurepseudonormal sign4mindepthreadbackshell creation is separate for modularity; the scan pass is simple because every volume is a tetrahedron

第 1 / 4 步 · CPU 为每个三角形生成覆盖窄带 Voronoi 区域的 tetra shell

逐步观察 T4 方法如何把三角形的窄带覆盖变成可查询的 SDF 网格。

5. “一次扫描”其实是对并行职责的重新分工

T4 方法把最适合硬件的部分交给 GPU:截面光栅化、fragment-level feature case analysis,以及 color/depth 的竞争选择。CPU 仍负责 shell 生成、z-sweep 的状态维护和每层 readback。这个分工解释了为什么它简单而有效,也解释了为什么优化方向包括更紧的 tetra shell、一次性 flatten 3D texture,或把更多阶段搬到 geometry shader。

对于同一个 voxel,多个三角形的 tetra shell 可能同时产生候选。不要用“最后写入者胜出”覆盖它们;要比较绝对值,同时保留获胜候选的 signed value。这样负号不会因为光栅化顺序而被偶然抹掉。

6. 用 SDF Lab 比较分辨率、窄带与拓扑风险

GPU Gems 3 · Chapter 34

T4 Signed Distance Field Lab

可交互

切换 mesh 拓扑、网格分辨率、窄带宽度、执行模式和查询目标,观察覆盖量、z-slice 数与距离场构建代价。

mesh → tetra shell → z-slice → SDF voxelsurface mesh21,000 tetrahedrad(p) → query301,990 covered voxels · 256 z-slices · 11 ms modelpseudonormal sign path · balanced · depth-min GPU path
tetrahedra21,000
covered voxels301,990
z-slices256
queries / second43,000

先在 irregular / holesGPU depth-min path 下扩大窄带,观察覆盖体素与构建时间;再把 mesh 切到 folded overlap,注意 Lab 会明确显示“folds need a second pass”。这不是性能惩罚,而是算法边界:当两个重叠面都能成为最近候选,单 pass 无法从距离大小唯一推出正确 inside/outside。

小结

  • SDF 在规则网格上保存最近有符号距离:表面为零,内部通常为负,外部通常为正;窄带把计算限制在表面附近。
  • T4 方法用每个三角形的 OBB 分解出的五个 tetrahedra 覆盖窄带,再沿 z 平面切出凸多边形。
  • fragment program 不把所有样本都当成 face-plane 距离,而是先定位 closest feature,再用 angle-weighted pseudonormal 判断符号。
  • color buffer 保存 signed distance,depth buffer 保存绝对距离;depth-min selection 让硬件选择同一 voxel 的最近候选。
  • single-pass 解决了 plane-test 等符号泄漏,但 folded topology 仍可能需要额外 pass;它也不意味着取消 z-slice 循环和 readback。

练习

练习

问题 1|修改 Demo 代码。 保持 irregular mesh,比较 128³256³512³,再比较窄带 20 与 60 voxels。记录 covered voxels、z-slices 和 queries/s,并解释为什么三维分辨率的增长比线性更快。

问题 2|诊断符号泄漏。 某个尖锐 ridge 的 SDF 在表面内侧出现正值。请按“最近 feature → 符号方向 → buffer 选择”的顺序列出检查项。

问题 3|场景选型。 一个布料碰撞系统、一个只做射线可见性查询的静态模型、一个包含折叠重叠面的扫描模型,分别选择 SDF 参数和后处理策略。

名词解释

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

signed distance field (SDF)

在网格上保存到表面最近有符号距离的标量场,通常内部为负、表面为零、外部为正。

narrow band

围绕表面的有限厚度壳层,只在用户指定距离内生成和扫描距离值。

T4 GPU scan

先生成 tetrahedral shell,再按 z 平面切片并在 GPU 上计算 signed distance 的扫描流程。

closest feature

对采样点而言距离最小的三角形面、边或顶点,是距离 case analysis 的输出。

angle-weighted pseudonormal

从顶点或边邻域法线按角度加权得到的局部方向,用于更可靠的 inside/outside 符号判断。

depth-min selection

用绝对距离做 depth、用带符号距离做 color,让 depth test 选择同一 voxel 的最近候选。

讨论

评论区加载中…