用 Canvas 分形噪声生成一个零依赖的风景页面:从值噪声到可复现的晨昏配色
为什么要自己写噪声
做程序化风景时,最省事的做法是用 Three.js 或现成的 Perlin 库,但一个静态风景页其实不需要 WebGL,也不需要构建工具。Value Noise(值噪声)实现只有十几行,配上分形叠加(fbm)就能得到足够自然的山脊线。
值噪声的核心
思路是:把连续坐标映射到整数格点,每个格点用种子化的哈希函数给出一个伪随机值,格点之间做平滑插值。哈希用整数位混合而不是 Math.random,这样同一个种子在任何时刻、任何机器上都得到同一张图。
function hash2(x, y, seed) {
let h = x * 374761393 + y * 668265263 + seed * 1442695040888963407 % 0x7fffffff;
h = (h ^ (h >> 13)) * 1274126177;
return ((h ^ (h >> 16)) >>> 0) / 4294967295;
}
插值用 fade 曲线 t*t*(3-2t) 而不是线性插值,否则格点处会出现明显折角,这也是很多人第一次手写噪声时山脊看起来「机械」的原因。
分形叠加
单层噪声只有一种尺度,山体看起来像起伏的丘陵。把 octave 叠起来:
function fbm(x, y, seed, octaves = 5) {
let amp = 1, freq = 1, sum = 0, norm = 0;
for (let i = 0; i < octaves; i++) {
sum += amp * noise(x * freq, y * freq, seed + i * 131);
norm += amp;
amp *= 0.5;
freq *= 2.0;
}
return sum / norm;
}
四层山体由远及近,振幅从 0.11 递增到 0.20,频率递减——远景平缓、近景起伏大,符合大气透视的直觉。每层用基色向天空色做一次线性混合,越远越接近天空色,雾感就出来了。
时辰与天气:把氛围拆成两张表
把「几点」和「天气」当成两个独立维度,各维护一张配色表,组合起来就是 5×6=30 种氛围,不需要为每种组合单独调色。天气额外控制云量、雾浓度、雨雪粒子与星辰/极光强度,这些只是叠加在构图之上的半透明图层,不影响地形生成。
可复现比好看更重要
调试阶段的痛点是「刚才那帧挺好但找不回来了」。所以种子必须从 URL 参数或元数据文件读入,而不是每次调用 Math.random。我们把它落到 .scene.json 里,顺手带上了时辰、天气、作品名与简介——这样一帧画面就是一个可重放的完整描述。
单文件交付的好处
最终产物是一个 index.html,没有任何外链与依赖,直接双击就能看,也方便丢到静态托管或塞进作品包。相对的代价是代码全在一个文件里,所以生成器本身用 Python 分层写好,HTML 只是渲染出来的结果——可读性与交付体积两头都要。
小结
程序化画面的门槛不在数学,而在约束:先定下「零依赖、可复现、单文件」这三条硬约束,剩下的实现选择基本是被逼出来的最优解。
- 点赞
- 收藏
- 关注作者
评论(0)