p5.js 二维 Perlin 噪声(2D Noise)实战指南:从一维到二维的逐像素生成原理

发布时间:2026/10/14 18:07:09
p5.js 二维 Perlin 噪声(2D Noise)实战指南:从一维到二维的逐像素生成原理
教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载Perlin 噪声Perlin Noise是创意编程中生成平滑、自然、非随机抖动效果的核心算法而二维噪声则把这种平滑性从一条曲线扩展到一个平面。本篇文章以 Coding TrainThe Coding TrainPerlin 噪声系列第 0.5 节 2D Noise 为骨架结合仓库中可运行的 p5.js 源码讲解如何在 p5.js 中通过noise(xoff, yoff)逐像素绘制一幅灰度噪声图并给出inc、xoff、yoff等关键参数的取值规律以及从 2D 升级到 3D 地形渲染的延伸思路。读完本文你将能够独立写出「像素级二维噪声纹理」并理解它为什么是云雾、地形、流体等效果的基础。本讲在系列中的位置从一维噪声到二维噪声0.5-2d-noise.md是仓库 Perlin 噪声学习系列 的第 5 讲编号 0.5该系列还包括0.2 引言Perlin 噪声是什么、如何产生以及历史背景0.3 noise() 与 random() 对比说明 p5.js 中两个「返回随机数」的函数本质差异0.4 一维噪声作图把一维噪声值画成平滑曲线并与纯随机对比0.5 二维噪声本文主题把一维噪声拓展到二维平面逐像素生成噪声纹理。理解 2D 噪声的关键在于先理解 1D一维噪声相当于沿着一条直线x 轴方向连续采样noise()的值得到的曲线是平滑波动的二维噪声则是在一个平面x-y 网格上连续采样每个采样点都拥有「相邻点相似」的特性从而形成连续渐变的空间纹理。配套的官方示例代码位于 learning/noise/0.5-2d-noise/P5/含 sketch.js 与 index.html仓库的旧版ES5 语法对应文件在 Tutorials/perlin_noise/04_perlin_noise_2D/。另外0.5-2d-noise.md的 front matter 中还记录了本讲的 p5.js Web Editor 草图 id2_hBcOBrF即线上可运行版本。核心思路用 xoff 与 yoff 遍历整个像素平面二维噪声相比一维噪声最大的变化是采样坐标从「一个递增的游标」变成「一组双游标」xoffx-offset沿画布 x 方向移动的噪声采样偏移量控制噪声在水平方向的变化快慢yoffy-offset沿画布 y 方向移动的噪声采样偏移量控制噪声在垂直方向的变化快慢。绘制策略是外层循环逐行扫描 y内层循环逐列扫描 x对每个像素调用noise(xoff, yoff)得到 0~1 之间的平滑值再乘以 255 映射为灰度写入像素数组。每扫描完一列xoff增加inc每完成一行yoff也增加inc。这样 x-y 平面上每个相邻像素对应的噪声采样点都「相邻」于是灰度是连续渐变的而不是 random() 那种杂乱跳变。这就是本讲最核心的算法骨架双层循环 双偏移游标 像素数组写入。逐行解读官方示例源码以下为 learning/noise/0.5-2d-noise/P5/sketch.js 的完整 ES6 版本该版本较旧版做了语法升级逻辑一致// 2D Noise // The Coding Train / Daniel Shiffman // This example has been updated to use es6 syntax. let inc 0.01; function setup() { createCanvas(200, 200); pixelDensity(1); } function draw() { let yoff 0; loadPixels(); for (let y 0; y height; y) { let xoff 0; for (let x 0; x width; x) { let index (x y * width) * 4; // let r random(255); let r noise(xoff, yoff) * 255; pixels[index 0] r; pixels[index 1] r; pixels[index 2] r; pixels[index 3] 255; xoff inc; } yoff inc; } updatePixels(); //noLoop(); }关键点逐行拆解let inc 0.01;——噪声增量是整个效果的「旋钮」。inc越小相邻采样点越近噪声越平滑、细节越少更像大片云层inc越大噪声越细碎、颗粒感越强。官方示例取0.01在 200×200 画布上意味着整个采样区间只有约 2 个单位200 × 0.01整体非常平滑。createCanvas(200, 200); pixelDensity(1);——创建 200×200 的画布并把像素密度强制设为 1。pixelDensity(1)在这里很重要若显示器为高分屏Retina 等p5.js 默认像素密度可能大于 1导致pixels数组长度与width * height不匹配、出现采样错位设为 1 可保证「每个像素恰好对应一个噪声采样点」。loadPixels()/updatePixels()——p5.js 像素操作的固定配对。loadPixels()把画布像素读入全局pixels数组updatePixels()在修改完毕后把数组写回画布。像素索引公式let index (x y * width) * 4;——p5.js 的pixels数组按 RGBA 排列每个像素占 4 个连续元素index0为 R红、index1为 G绿、index2为 B蓝、index3为 A透明度。let r noise(xoff, yoff) * 255;——二维噪声的核心调用。noise()返回 0~1 之间的平滑值乘 255 映射到 0~255 的灰度区间。将同一个r同时赋给 R、G、B 三分量即得到灰度图pixels[index 3] 255保证完全不透明。游标推进——内层循环每处理一个像素xoff inc外层循环每换一行yoff inc。注意两个方向使用相同的inc因此噪声在 x、y 两个方向上的「缩放」一致若想让水平/垂直方向细节密度不同可分别使用incX与incY例如地形示例中xoff 0.2、yoff 0.2。被注释掉的// let r random(255);——官方刻意保留这行注释用于对比换成random(255)后每个像素的灰度完全随机、相邻像素毫无关联画面变成雪花噪点而noise()版本则是连续渐变纹理。这正是系列第 0.3 讲 noise() 与 random() 对比 在二维场景下的直观印证。被注释掉的//noLoop();——若取消注释draw()只执行一次得到一帧静态噪声图保留注释则每一帧重绘。由于yoff每帧都从 0 重新开始没有像一维作图那样维护全局start画面本身是静止的。运行入口 index.html 通过 CDN 引入p5.min.js仓库内为p51.4.1后加载sketch.js直接在浏览器打开即可看到 200×200 的灰度噪声纹理。仓库内新旧版本对照仓库中保存了本讲的两个历史版本适合对照学习版本位置语法差异要点新版本learning/noise/0.5-2d-noise/P5/sketch.jsES6let/const注释更丰富含//noLoop()说明旧版本Tutorials/perlin_noise/04_perlin_noise_2D/sketch.jsES5var逻辑相同是 2016 年视频录制时的原始形态旧版 HTMLTutorials/perlin_noise/04_perlin_noise_2D/index.html还额外引入了p5.sound.min.js音频插件与噪声绘制无关仅说明当时教程的运行环境。两版核心算法完全一致从源码结构可以推断该讲重点始终是「用双层循环把二维噪声写进像素数组」这一个模式。关键参数调优inc、画布尺寸与灰度映射二维噪声的效果由三个因素决定官方示例均给出明确取值inc 0.01官方默认采样增量。建议尝试区间 0.005 ~ 0.05。经验规律inc越小 → 采样越密集、过渡越平滑 → 大块「云团」式纹理inc越大 → 采样越稀疏、细节越丰富 → 细碎「沙粒」式纹理若inc过大如 0.1相邻像素噪声值几乎无关视觉上退化为随机噪点失去 Perlin 噪声的意义。画布尺寸 200×200官方选择小画布一是便于观察全貌二是在inc 0.01下采样区间约为[0, 2]恰好落在噪声函数平滑变化的核心区间内。若改为 600×600建议同步调小inc如 0.005以保持「整幅画面只有少数几片大云层」的观感。灰度映射noise(...) * 255noise()输出 0~1乘以 255 得到 0~255 灰度。若想观察更极端的明暗对比可用map(noise(xoff, yoff), 0, 1, 50, 200)压缩灰度范围若想得到彩色纹理可分别用三个不同偏移如noise(xoff, yoff)、noise(xoff 100, yoff)、noise(xoff, yoff 100)赋给 R/G/B。延伸从 2D 噪声到 3D 地形仓库源码佐证二维噪声最著名的进阶应用是「Perlin 噪声地形」仓库中就有完整的可运行实现CodingChallenges/CC_011_PerlinNoiseTerrain/P5/sketch.js。该示例与本讲一脉相承把二维噪声从「像素平面」升级为「顶点高度场」var yoff flying; // 第三维随时间变化的起点 for (var y 0; y rows; y) { var xoff 0; for (var x 0; x cols; x) { terrain[x][y] map(noise(xoff, yoff), 0, 1, -100, 100); xoff 0.2; } yoff 0.2; }对照本讲可以清楚看到三个升级点同样的双游标双层循环xoff每列递增、yoff每行递增采样结构与 2D 噪声完全一致用map()而非* 255把噪声值 0~1 映射到 -100~100作为地形顶点高度第三维动画yoff不再每帧从 0 开始而是以全局变量flying为起点并每帧flying - 0.1于是整块噪声场沿 y 方向「流动」形成飞越地形的效果——这就是「3D 噪声 2D 噪声切片随时间推移」的直观演示。反过来如果在 0.5 讲中取消//noLoop()注释并让yoff每帧偏移同样能让 2D 噪声纹理动起来得到类似流云的效果。系列其他相关实现还包括 CC_036_Blobby、CC_060_Butterfly_Wings、CC_103_Flames 等均可在仓库中检索到noise(xoff, yoff)的同类用法可作为进一步研究二维/三维噪声的素材。常见问题排查结合源码可以归纳本讲最易出现的三个问题画面出现条纹或错位通常是没有调用pixelDensity(1)高分屏下pixels数组长度超过width * height导致采样索引错位。解决方案即官方写法pixelDensity(1)。忘了loadPixels()/updatePixels()直接读写pixels数组而不配对调用这两个函数画面不会更新或直接报错。看起来像随机噪点inc取值过大导致相邻采样点失去连续性。回到inc 0.01附近并检查是否误把noise()写成了random()官方注释中特意保留了random(255)的对比行。小结0.5-2d-noise.md这一讲的技术要点可以概括为一句话用noise(xoff, yoff)配合双层循环把二维 Perlin 噪声逐像素写入pixels数组即可得到平滑渐变的灰度纹理。核心参数inc控制平滑度与细节密度pixelDensity(1)保证像素映射正确xoff/yoff双游标则是从一维到二维的关键跳跃。掌握这一模式后向上可扩展出 3D 地形见 CC_011向旁可延伸出流动纹理与彩色噪声是进入 Perlin 噪声创意编程的重要一步。完整代码与运行入口请查看 learning/noise/0.5-2d-noise/P5/。赞分享教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载相关推荐用 Perlin 噪声打造二维流场Coding Challenge 24 的 p5.js 与 Processing 完整实现用 Perlin 噪声打造二维流场Coding Challenge 24 的 p5.js 与 Processing 完整实现 本篇技术指南围绕 The Cod教程文档示例工程OpenSimplex2二维至四维的噪声生成库OpenSimplex2二维至四维的噪声生成库 基础介绍 OpenSimplex2 是一个开源的噪声生成库由 KdotJPG 开发旨在提供一种改进的 Si探索OpenSimplex2下一代二维与三维噪声库探索OpenSimplex2下一代二维与三维噪声库 是一个开源的、跨平台的噪音生成库由K. Jason GSKdotJPG开发。如果你是游戏开发者、图形创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考