GLSL值噪声函数中两种br写法为何结果不同且存在断层?
为什么GLSL中
vec2(bl.x+1., bl.y)和bl + vec2(1.,0.)会导致值噪声出现断层差异? 问题现象
在实现GLSL值噪声时,两种看似等价的br坐标计算方式,却产生了完全不同的结果:
- 使用
vec2 br = vec2(bl.x+1.,bl.y)时,输出图像出现明显断层; - 改成
vec2 br = bl + vec2(1.,0.)后,断层完全消失。
且仅修改br变量时有此影响,修改tl、tr的构造方式不会触发问题。
初始代码(存在断层)
float valueNoise(vec2 v, float n){ v*=n; vec2 bl = floor(v); vec2 fr = fract(v); vec2 br = vec2(bl.x+1.,bl.y); vec2 tl = vec2(bl.x,bl.y+1.); vec2 tr = vec2(bl.x+1.,bl.y+1.); float top = mix(random2D(tl),random2D(tr), fr.x); float bottom = mix(random2D(bl),random2D(br), fr.x); return mix(bottom, top, fr.y); }
修改后代码(修复断层)
float valueNoise(vec2 v, float n){ v*=n; vec2 bl = floor(v); vec2 fr = fract(v); vec2 br = bl + vec2(1.,0.); vec2 tl = vec2(bl.x,bl.y+1.); vec2 tr = vec2(bl.x+1.,bl.y+1.); float top = mix(random2D(tl),random2D(tr), fr.x); float bottom = mix(random2D(bl),random2D(br), fr.x); return mix(bottom, top, fr.y); }
原因分析
这是浮点数精度误差导致的,核心差异在于GLSL对两种运算的处理逻辑:
分量单独运算的隐患:
bl是floor(v)的结果,表面上是整数,但当v的数值很大时,某些整数无法被32位浮点数精确存储(比如大于2^24的整数)。此时bl.x+1.的计算可能因为精度损失,得到一个比预期整数小一点点的浮点数(例如本该是10000000的结果,实际存储为9999999.999999998)。当这个值传入random2D()时,函数内部如果再次对坐标做取整/哈希处理,会被识别为和预期不同的整数坐标,导致噪声值突变,产生断层。向量加法的稳定性:
bl + vec2(1.,0.)是直接对整个向量做加法运算,GLSL的向量运算会保证整数浮点数的加法精度(只要数值在可精确表示范围内,整数加1的结果一定是精确的整数浮点数),不会出现上述的精度损失。因此random2D()拿到的是正确的相邻整数坐标,噪声过渡自然平滑。
补充建议
之所以修改tl、tr没出现问题,是因为测试场景中bl.y的数值刚好在浮点数精确表示范围内。如果后续处理更大的y轴坐标范围,tl = vec2(bl.x,bl.y+1.)也可能出现同样的断层问题。建议统一使用向量加法的方式计算所有相邻坐标,避免精度隐患:
vec2 br = bl + vec2(1., 0.); vec2 tl = bl + vec2(0., 1.); vec2 tr = bl + vec2(1., 1.);
内容的提问来源于stack exchange,提问作者Ivan Kucha
相关产品推荐
相关产品推荐

