GPU Gems 2 · Chapter 8. Per-Pixel Displacement Mapping with Distance Functions

用 3D distance map 为 tangent-space ray 提供保守步长,在 pixel shader 中执行 sphere tracing,实现带遮挡和非多边形轮廓的细节 displacement。

学习目标

  • 能区分 bump/normal mapping、固定步长 relief mapping 与 distance mapping 的几何能力
  • 能解释 3D distance map 如何给 sphere tracing 提供不会越过表面的安全步长
  • 能把 vertex shader 的 tangent-space ray、fragment shader 的迭代和命中后的纹理采样串起来
  • 能指出 displaced UV 导数不连续时的过滤策略,以及 displacement depth 与迭代预算的边界

Bump mapping 只改变光照法线,不能让一个表面特征遮挡另一个特征,也不能改变 silhouette。displacement mapping 改变实际表面,但传统做法需要持续 tessellate,顶点 shader 又不能生成新顶点。

本章换一个方向:把平面或局部切平面当作屏幕 ray 的起点,在 fragment shader 中求这个 ray 与 displaced surface 的交点。这样工作量随屏幕像素而不是模型顶点增长。

1. 从 height map 到 distance map

是一个体数据。height map 只给定 (u, v) 的高度,distance map 则对体内每个点提供到表面的最短距离;只要当前点在表面外,沿任意方向走不超过这个距离,就不会越过表面。

Height field → 3D distance mapheight map每个 UV 一个高度distance map每个 3D 点到表面的最短距离
高度图只告诉你某个 UV 的高度;distance map 还告诉 ray 离表面有多远,因此能安全跳步。

距离变换可以通过多次邻域 sweep 生成,每个体素先保存指向最近表面的位移向量,最后取向量长度并归一化到纹理深度范围。预处理成本换来运行时可跳过大量空空间的安全步长。

2. sphere tracing:距离驱动的 ray marching

从起点 p0 查询 dist(p0, S),再令 p1 = p0 + distance * rayDirection。重复这个步骤,远离表面时步子大,接近表面时自动变小;它不会像固定步长那样跨过一个比步长更薄的 feature。

Sphere tracing:距离就是安全步长p0p1p2p3p4p5p next = p + dist(p, surface) × normalized ray
每一步长度不超过当前位置到表面的安全距离,所以不会像固定步长那样跨过薄的特征。

通常设置固定的最大迭代次数,在距离小于命中阈值时提前结束。迭代预算要和 displacement depth、视角和 distance map 分辨率一起调;次数太少会提前终止,太多会增加 dependent texture read。

3. tangent space 中的 shader 路径

让平面算法推广到一般 mesh。vertex shader 将 eye vector 投影到 tangent、binormal、normal 三个基向量上,同时传递 base texture coordinate;fragment shader 以它为 ray 起点和方向。

Base surface → ray march → displaced shadingVertexbase UV + tangent eye rayDistancesafe step in texture spaceFragmentiterate ray intersectionLightingnormal map at hit UV局部 tangent frame 让平面算法也能用于一般 mesh
顶点阶段只准备 ray 起点和方向;真正的 displacement intersection 发生在 fragment shader。

命中 UV 之后,fragment shader 采样 color texture 和 normal texture,继续做 tangent-space lighting。注意 pixel displacement 只能在已有 rasterized surface 的像素范围内改变采样结果,不能承担任意大的位移轮廓。

4. 过滤与坐标导数

Filtering:命中 UV 跳变,导数不能照抄displaced UV边缘处不连续 → 错 mipbase UV derivatives颜色 / 法线按 base UV 过滤
命中点 UV 可能跨过遮挡特征发生跳变,直接用它的 derivatives 会选错 mip;用未扰动 UV 的导数更稳定。

在 displacement 命中点发生跳变时更可靠。用 ddxddy 计算 base UV 的导数,再把它们显式传给颜色与 normal map 采样;不要直接让 GPU 从 discontinuous hit UV 推断。

distance map 的距离值本身不能粗暴使用 mipmap,因为过粗的 level 会把安全距离抹平。常见策略是对 distance map 做线性过滤但不生成 mipmap;颜色和法线则继续用 base UV 导数选择合适层级。

5. 动手实验:安全步长与迭代预算

调大 displacement depth 或让视角更掠射,观察需要的追踪步骤趋势;再降低最大迭代次数,判断何时会提前停止。实验数值用于理解控制变量,真实实现仍需依据 texture size、shader 指令和目标 GPU profile。

Distance mapping 实验

观察 ray 方向、distance map 深度和迭代上限对命中过程的影响。

tangent-space ray · 7 stepsview angle 42° · depth 32 · max iterations 12

预计追踪 7 步。固定迭代上限过低会提前停止,过高则增加每像素 dependent texture reads。

小结

  • distance mapping 将屏幕 pixel displacement 变成局部 ray-intersection 问题。
  • distance map 提供到表面的安全距离,sphere tracing 因此能跳过空空间且避免 overshoot。
  • tangent space 让同一算法适用于局部平面化的普通 mesh。
  • pixel shader 适合有界小位移,不会改变既有像素的屏幕轮廓。
  • hit UV 可能不连续,颜色和法线应使用 base UV derivatives,distance map 要谨慎过滤。

练习

问题 1|步长安全 为什么在 surface 外使用 distance map 的值作为步长不会直接穿过表面?

问题 2|坐标变换 为什么 vertex shader 要把 viewing ray 转到 tangent space,而不是直接用世界空间 ray 查询 height map?

问题 3|过滤 命中 UV 的导数在遮挡边缘跳变,直接用它选 mipmap 会发生什么?如何修?

名词解释

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

distance map
保存体内点到 displaced surface 最近距离的 3D 纹理。
sphere tracing
使用安全距离作为 ray 步长并逐步收敛的追踪方法。
ray marching
沿 viewing ray 迭代采样并更新纹理坐标的过程。
tangent space
由切线、副切线和法线构成的局部坐标系。
base UV derivatives
由未扰动 UV 的屏幕有限差分得到的稳定采样导数。
pixel displacement
在 fragment shader 中改变命中采样与光照、但不生成新屏幕顶点的位移表现。

讨论

评论区加载中…