Parallax Mapping 通过视线方向在切线空间中对纹理坐标进行高度加权偏移实现视差效果,核心公式为偏移量 = viewDirTangent.xy / viewDirTangent.z height scale;必须使用归一化TBN矩阵将视线和光照向量变换至切线空间,推荐采用多层采样POM提升稳定性,并确保高度图格式、坐标约定与着色器一致。

Parallax Mapping 的核心是视线方向偏移纹理坐标
视差贴图不是真正改变几何,而是利用高度图(heightMap)在采样时沿视线方向“错开”uv,让高处像素看起来更靠近摄像机、低处更远。关键不是加法偏移,而是用视线在切线空间的 xy 分量对高度做加权缩放:偏移量 = viewDirTangent.xy / viewDirTangent.z * height * scale。漏掉除以 z 会导致近处拉伸、远处失效——这是最常被抄错的点。
必须用切线空间(TBN)才能正确计算视差偏移
直接用世界或模型空间的视线向量会随物体旋转而错乱。必须把 viewDir 和 lightDir 都变换到每个像素的切线空间,靠顶点着色器传入的 T(切线)、B(副切线)、N(法线)构成 TBN 矩阵。常见错误是只传了法线,没传或没归一化 T 和 B;或者在片元着色器里用未插值归一化的 TBN 矩阵转 viewDir,导致边缘扭曲。务必在顶点着色器中对 T、B 做 normalize(),再传给片元着色器。
用多层采样(Occlusion Mapping)比单次偏移更稳定
基础 Parallax Mapping 单次偏移容易出现“浮空”或“剪切”伪影,尤其在大角度观察时。推荐改用 parallaxOcclusionMapping(POM):沿视线方向分步采样高度图,找到第一个满足 height > currentDepth 的深度层,用该层对应的 uv 采样颜色。步骤包括:
- 预计算最大步数(如
16)和每步深度增量(1.0 / float(numSteps)) - 从
uv开始,按stepSize * viewDirTangent.xy / viewDirTangent.z累加偏移 - 每次采样
heightMap,比较sampledHeight > currentDepth,用二分查找收敛精度 - 最终用插值得到的
uv采样albedoMap和normalMap
不建议手写完整 POM 循环——GLSL 中分支代价高,优先用预烘焙的 heightMap + normalMap 混合,或降级为 steepParallaxMapping(固定 8–12 步)。
立即学习“C++免费学习笔记(深入)”;
C++ 侧只需准备正确的顶点数据与 uniform 上传
C++ 本身不参与着色逻辑,但决定效果上限:若顶点没提供 texCoord、normal、tangent(含 bitangent 符号),GPU 端再精细也没用。关键检查项:
-
tangent属性必须是vec4,w分量存bitangent方向(+1 或 -1),用于构造右手系 TBN - 高度图必须是单通道
GL_R8或GL_R16,避免压缩格式(如GL_COMPRESSED_RGBA_S3TC_DXT5)破坏精度 -
parallaxScaleuniform 通常设为0.02–0.08(砖墙建议0.04),过大则漂浮,过小无感 - 确保着色器中
viewPos是世界坐标,且fragPos也统一在世界空间计算viewDir
最容易被忽略的是:高度图的黑/白含义必须和着色器一致——多数实现约定“白色=高”,但若美术导出时反相,整个表面会内凹成洞。上线前务必用纯灰度图(0.0/0.5/1.0)验证高度响应是否符合预期。


















