POM是通过视线方向多步ray-marching搜索高度图交点的视差技术,区别于普通视差贴图的单次线性偏移;它支持陡峭斜面、无错位遮蔽,但需fragment shader中循环采样。

什么是 POM,它和普通视差贴图的区别在哪
POM 不是简单位移纹理坐标(像 parallax mapping 那样只做一次粗略偏移),而是通过在高度图上沿视线方向做多步 ray-marching,找到视线与表面微几何的实际交点。这意味着它能正确遮蔽、支持陡峭斜面、边缘无错位——但代价是必须在 fragment shader 里跑循环。
普通视差贴图(parallax mapping)只用一个高度值线性缩放视线水平分量,结果在大角度下明显“浮起”或穿模;而 parallax occlusion mapping 每次迭代都采样 heightMap,比较当前深度与视线累计深度,直到找到最靠近摄像机的不透明层。
关键区别在于:POM 的偏移量不是算出来的,是搜出来的。
GLSL 中实现 POM 的核心循环怎么写
核心是视线在切线空间中沿 viewDir 方向逐步试探,每步用当前 texCoord 采样高度图,判断是否“已穿透”该层。常见写法是固定步数(如 10–20 步)或带 early-out 的自适应步进。
立即学习“C++免费学习笔记(深入)”;
vec2 p = viewDir.xy / viewDir.z * heightScale; // 单位深度对应的最大水平偏移
vec2 delta = p / float(numSteps);
vec2 curTexCoords = texCoord;
float curHeight = 0.0;
float h = texture(heightMap, curTexCoords).r;
<p>for (int i = 0; i < numSteps; i++) {
curTexCoords -= delta;
h = texture(heightMap, curTexCoords).r;
curHeight = float(i) / float(numSteps);
if (h > curHeight) break;
}</p>-
heightScale是缩放因子,通常 0.02–0.08,太大会导致过度偏移,太小则无效果 - 循环中用
curHeight = float(i) / float(numSteps)模拟从摄像机出发的归一化深度,不是真实世界深度 - 务必用
texture(heightMap, curTexCoords).r,别漏掉.r—— 高度图一般存为单通道红通道 - WebGL 或旧 GPU 上需避免动态循环次数,
numSteps必须是编译期常量(如#define NUM_STEPS 16)
为什么 POM 在边缘常出现闪烁或黑边
本质是采样精度与步长不匹配:当表面接近掠射角时,视线在纹素间跨越过快,单次 delta 可能跳过高度突变区域;同时,texture() 默认双线性插值会把悬崖边缘“抹平”,导致采样值虚高,提前终止搜索。
- 解决方法一:改用
textureLod(heightMap, curTexCoords, 0.0)关闭 LOD 和插值,强制最近邻采样(更锐利,更适合高度图) - 解决方法二:在循环末尾加半步微调(bilinear interpolation between last two samples),用前后两次采样做线性反推交点,显著提升边缘精度
- 高度图本身不能有压缩伪影 —— 用
GL_R16或GL_R32F格式加载,禁用 S3TC/BC 等有损压缩
C++ 侧需要做什么(不是写 shader!)
C++ 层不参与 POM 计算逻辑,但必须确保 GPU 能拿到正确数据:
- 顶点着色器输出必须包含完整切线空间基:即
T(切线)、B(副切线)、N(法线),并传给 fragment shader —— 少一个向量,viewDir就无法转到切线空间 - 高度图要作为
GL_TEXTURE_2D绑定到单独纹理单元(如GL_TEXTURE1),并在 shader 中声明为sampler2D heightMap - 如果用 ASSIMP 加载模型,需显式请求切线计算:
aiProcess_CalcTangentSpace,否则mesh->mTangents为空 - 不要尝试在 C++ 里预计算 POM 偏移 —— 这是典型的“把 fragment 工作塞到 CPU 做”,既慢又错,所有偏移必须实时在 shader 中完成
POM 的复杂性全在 fragment shader 的数值稳定性上:步数、高度缩放、采样方式,三者稍不匹配就会在斜面处崩出条纹或悬浮块。真正难的不是写出来,而是让不同视角、不同曲率下都收敛到合理交点。


















