如何降低Perlin噪声纹理的平铺感同时保持分辨率无关特性
噪声纹理平铺重复问题解决方案
你所用的噪声库采用经典Perlin噪声实现,默认渐变查找表长度为256,当采样坐标超过256时会进入周期循环,因此会出现可感知的平铺规律。以下方案均可在保留分辨率无关特性的前提下削弱重复感:
- 多层分形噪声(FBM)叠加
原理是将多个不同缩放比例、不同振幅的噪声结果加权相加,大幅拉长整体重复周期,同时还能让噪声纹理更自然。修改后的采样逻辑如下:
// 固定FBM参数,保证分辨率无关性 const FBM_OCTAVES = 4; // 叠加层数 const FBM_LACUNARITY = 2.13; // 层间频率缩放系数,用非整数避免周期重合 const FBM_GAIN = 0.58; // 层间振幅衰减系数 function getFbmNoise(x, y, z) { let sum = 0; let amp = 1; let freq = 1; let maxSum = 0; // 用于归一化结果到0-1区间 for(let i = 0; i < FBM_OCTAVES; i++) { sum += rd.noise.get(x * freq, y * freq, z) * amp; maxSum += amp; amp *= FBM_GAIN; freq *= FBM_LACUNARITY; } return sum / maxSum; } let grain_index = 0; let grain_scale = 0.0025; for (let ypos = 0; ypos < rd.canvas.height; ypos++) { for (let xpos = 0; xpos < rd.canvas.width; xpos++) { let samplex = xpos / (rd.canvas.height * grain_scale); let sampley = ypos / (rd.canvas.width * grain_scale); let samplez = 0; let lum = getFbmNoise(samplex, sampley, samplez) * 255; grain_imagedata[grain_index + 0] = lum; grain_imagedata[grain_index + 1] = lum; grain_imagedata[grain_index + 2] = lum; grain_imagedata[grain_index + 3] = 96; grain_index += 4; } }
所有参数均为固定常量,采样坐标仍沿用原有和画布尺寸绑定的归一化逻辑,不会破坏分辨率无关特性。
- 多噪声源加权混合
同时采样两个不同缩放比例的噪声,混合后整体周期会变为两个噪声周期的最小公倍数,远大于单个噪声的256周期,肉眼几乎无法识别重复:
let grain_index = 0; let grain_scale = 0.0025; // 固定缩放系数,使用非整数值避免周期对齐 const NOISE_SCALE_2 = 1.47; const MIX_RATIO = 0.5; for (let ypos = 0; ypos < rd.canvas.height; ypos++) { for (let xpos = 0; xpos < rd.canvas.width; xpos++) { let samplex = xpos / (rd.canvas.height * grain_scale); let sampley = ypos / (rd.canvas.width * grain_scale); let samplez = 0; let lum1 = rd.noise.get(samplex, sampley, samplez); // z轴加固定偏移避免两个噪声同源相关 let lum2 = rd.noise.get(samplex * NOISE_SCALE_2, sampley * NOISE_SCALE_2, samplez + 100); let lum = (lum1 * (1 - MIX_RATIO) + lum2 * MIX_RATIO) * 255; grain_imagedata[grain_index + 0] = lum; grain_imagedata[grain_index + 1] = lum; grain_imagedata[grain_index + 2] = lum; grain_imagedata[grain_index + 3] = 96; grain_index += 4; } }
- 调整全局采样偏移
如果不想改动噪声计算逻辑,可以在采样坐标上叠加固定的大数值偏移,避免采样范围刚好落在整数周期边界上,一定程度上削弱平铺的规律性:
let grain_index = 0; let grain_scale = 0.0025; // 固定全局偏移量,不要动态生成,保证分辨率无关 const GLOBAL_OFFSET = 1234.56; for (let ypos = 0; ypos < rd.canvas.height; ypos++) { for (let xpos = 0; xpos < rd.canvas.width; xpos++) { let samplex = xpos / (rd.canvas.height * grain_scale) + GLOBAL_OFFSET; let sampley = ypos / (rd.canvas.width * grain_scale) + GLOBAL_OFFSET * 1.7; let samplez = 0; let lum = rd.noise.get(samplex, sampley, samplez) * 255; grain_imagedata[grain_index + 0] = lum; grain_imagedata[grain_index + 1] = lum; grain_imagedata[grain_index + 2] = lum; grain_imagedata[grain_index + 3] = 96; grain_index += 4; } }
所有方案的核心是所有缩放、偏移参数均使用固定常量,且采样坐标保持和画布尺寸的归一化绑定,因此不会破坏原有的分辨率无关特性,任意分辨率下生成的纹理视觉表现完全一致。
你当前代码生成的带平铺问题的噪声纹理如下:
内容的提问来源于stack exchange,提问作者David Wilson
相关产品推荐
相关产品推荐

