用 Canvas 分形噪声生成一个零依赖的风景页面:从值噪声到可复现的晨昏配色

举报
yd_296587128 发表于 2026/10/29 11:34:20 2026/10/29
【摘要】 用值噪声与分形叠加(fbm)在纯 Canvas 上生成山体轮廓,用一套时辰+天气的配色表控制氛围,全部逻辑不到 200 行、零依赖,同一随机种子可复现同一帧画面。

为什么要自己写噪声

做程序化风景时,最省事的做法是用 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 只是渲染出来的结果——可读性与交付体积两头都要。

小结

程序化画面的门槛不在数学,而在约束:先定下「零依赖、可复现、单文件」这三条硬约束,剩下的实现选择基本是被逼出来的最优解。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。