You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何降低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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 11:09:03