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
↡为纹理空间中的三维点保存其到 displaced surface 最近距离的 3D 纹理是一个体数据。height map 只给定 (u, v) 的高度,distance map 则对体内每个点提供到表面的最短距离;只要当前点在表面外,沿任意方向走不超过这个距离,就不会越过表面。
距离变换可以通过多次邻域 sweep 生成,每个体素先保存指向最近表面的位移向量,最后取向量长度并归一化到纹理深度范围。预处理成本换来运行时可跳过大量空空间的安全步长。
2. sphere tracing:距离驱动的 ray marching
↡使用到隐式表面的安全距离作为 ray 步长、逐步收敛到交点的追踪方法从起点 p0 查询 dist(p0, S),再令 p1 = p0 + distance * rayDirection。重复这个步骤,远离表面时步子大,接近表面时自动变小;它不会像固定步长那样跨过一个比步长更薄的 feature。
↡在像素阶段沿 viewing ray 反复更新纹理坐标、查询距离并向交点前进的过程通常设置固定的最大迭代次数,在距离小于命中阈值时提前结束。迭代预算要和 displacement depth、视角和 distance map 分辨率一起调;次数太少会提前终止,太多会增加 dependent texture read。
3. tangent space 中的 shader 路径
↡由表面切线、副切线和法线构成、用于把 view ray 与 light vector 转入局部纹理坐标的坐标系让平面算法推广到一般 mesh。vertex shader 将 eye vector 投影到 tangent、binormal、normal 三个基向量上,同时传递 base texture coordinate;fragment shader 以它为 ray 起点和方向。
命中 UV 之后,fragment shader 采样 color texture 和 normal texture,继续做 tangent-space lighting。注意 pixel displacement 只能在已有 rasterized surface 的像素范围内改变采样结果,不能承担任意大的位移轮廓。
4. 过滤与坐标导数
↡由原始未扰动纹理坐标的屏幕有限差分得到的采样导数,用于稳定选择颜色和法线纹理 mip level在 displacement 命中点发生跳变时更可靠。用 ddx 和 ddy 计算 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 深度和迭代上限对命中过程的影响。
预计追踪 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 中改变命中采样与光照、但不生成新屏幕顶点的位移表现。