中EN
精修课概念页

噪声:有记忆的随机

Noise Functions

更新于 2026-10-15 约 22 分钟噪声PerlinSimplexGLSL程序化

从雪花到「有记忆」的混乱

上一课的随机数彼此毫无关联,画出来就是一屏电视雪花。可真实世界里的「随机」不是这样的:路面的起伏、岩石的纹理、天上的云,都难以预测,却又连贯、平滑,相邻的地方总是彼此相似。

1980 年代初,Ken Perlin 为电影《电子世界争霸战》(Tron) 设计了一种算法,专门生成这种「看起来随机、却不像雪花」的纹理。这个算法后来让他获得了奥斯卡科学工程奖。这就是噪声 (noise)。

一维:只在格点上掷骰子

思路很简单:别给每个 x 都掷一次骰子,只在整数格点上掷。把 x 拆成整数部分和小数部分:

  • i = floor(x):左边的格点;
  • f = fract(x):在两个格点之间走了多远。

然后在 rand(i) 和 rand(i + 1.0) 两个随机高度之间插值。先用直线连,再把直线换成上一课就认识的 S 形曲线 f * f * (3.0 - 2.0 * f):

01234567
橙点是整数格点上的随机高度。虚线是直线插值,在格点处会突然拐弯;实线是 S 形曲线插值,经过格点时是平的,所以整条线又连续又平滑。

在下面的绘图器里依次点几个预设,看看一条随机折线是怎么变成平滑起伏的:

从台阶到起伏

直接改上面的表达式就能重画:x 是 0 到 1 的横坐标,可以用 sin、pow、smoothstep 等函数。

依次是:每格一个随机值(台阶);直线连起来(折线);S 形曲线连起来(值噪声);再随时间平移。缩放越大,起伏越密。

峰和谷之间不再生硬地拐弯,这就是图形里「有机感」的来源。

二维:插值正方形的四个角

一维插的是两个端点,二维就插正方形的四个角。三维是立方体的八个角,一般地说,N 维要插 2N2^N 个角。

abcdfmix(a, b, u.x)① 沿下边插值mix(c, d, u.x)② 沿上边插值mix(①, ②, u.y)③ 再沿竖直方向插值
四个角各有一个随机高度 a、b、c、d。先沿下边和上边各插一次(黄点),再在两者之间沿竖直方向插一次,就得到格子里那个像素(粉点)的值。
二维值噪声
改一改

也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。

黄线是整数格子的边界。把「平滑」拖到 0,就能看到直线插值留下的十字和菱形折痕。

值噪声和梯度噪声

仔细看值噪声,会发现它有一种方块感:亮斑和暗斑总是排在格子上。1985 年,Perlin 提出了梯度噪声 (gradient noise):格点上放的不再是随机的高度,而是随机的方向。

值噪声:角上放高度0.20.90.60.1梯度噪声:角上放方向
值噪声在每个角上放一个随机数;梯度噪声在每个角上放一个随机方向。像素的值由「从角指向像素的向量」和这个方向的点积决定。

每个角的贡献是 dot(随机方向, f - 角的位置):像素顺着这个方向越远,值越大。所有格点处的值都正好是 0,起伏全在格点之间,所以亮暗不再贴着格子排列。

左边值噪声,右边梯度噪声
改一改

也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。

左边的斑块排得方方正正,右边的起伏更像自然的纹理。梯度噪声的一个起伏只有半格左右大,所以右边把频率减半了,两边的斑点才差不多大。

Íñigo Quilez 在 Shadertoy 上做过值噪声和梯度噪声的并排对比,值得去看看。

更平的插值曲线

Perlin 后来还改进了经典噪声的插值:把三次曲线 3x2−2x33x^2 - 2x^3(也就是 smoothstep)换成五次曲线

6x5−15x4+10x36x^5 - 15x^4 + 10x^3

两条曲线在 0 和 1 处的斜率都是 0,但五次曲线在两端更平,格子之间的接缝更顺。上面梯度噪声的代码用的就是它。在绘图器里比一比:

三次曲线和五次曲线

直接改上面的表达式就能重画:x 是 0 到 1 的横坐标,可以用 sin、pow、smoothstep 等函数。

两条线乍看很像,区别在两端:五次曲线贴着 0 和 1 停留得更久。

Simplex:三个角就够了

2001 年的 Siggraph 上,Perlin 又提出了 Simplex Noise。按他的说法,它比前代有这些改进:

  • 乘法更少,计算复杂度更低;
  • 扩展到更高维度时代价小得多;
  • 方向性的瑕疵更少;
  • 梯度定义良好,容易计算;
  • 更适合用硬件实现。

关键在一个几何上的发现:要填满二维平面,不一定非得用正方形,最简单的形状是等边三角形。N 维的单纯形 (simplex) 只有 N + 1 个角。

正方形:4 个角单纯形:3 个角
正方形网格的每一格要插值 4 个角;把正方形斜切成三角形,每个三角形只需要 3 个角。
维度正方形 / 立方体的角单纯形的角
243
384
N2N2^NN+1N + 1

具体做法是:先把方格切成两个等腰直角三角形,再把整个网格斜着「推」一下,让三角形变成等边的;然后比较变换后的 x 和 y 谁大,就知道像素落在上面那个三角形还是下面那个,只插三个角。Ian McEwan 和 Stefan Gustavson 写过面向教学的 GLSL 实现和论文。

拿噪声来做点什么

噪声本身只是一片起伏,真正好玩的是把它接到前几课学过的工具上。

用噪声扭转空间:木纹

把噪声的值当成旋转角度,先把坐标扭一扭,再画一排平行的条纹。条纹被局部地扭向不同方向,看上去就像木头的年轮和纹理:

噪声 + 旋转 = 木纹0.0s
改一改

也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。

扭转为 0 时,只剩下一排笔直的条纹。慢慢往右拖,条纹开始打旋。

用噪声当距离场,调制轮廓

噪声的值也可以当成距离场:在某个阈值附近用 smoothstep 切一刀,就得到一块块墨点和孔洞。还可以用噪声去推一个圆的半径,让轮廓活起来:

一只会蠕动的小生物0.0s
改一改

也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。

小生物的半径由噪声决定;背景的蓝色墨点是把噪声当距离场切出来的。

在游戏里怎么用

取舍与常见的坑

小结与练习

  • 噪声只在格点上取随机值,格点之间平滑插值,所以它「有记忆」:相邻的地方彼此相似。
  • 一维插两个端点,二维插四个角,N 维插 2N2^N 个角;Simplex 用单纯形把角数降到 N + 1。
  • 梯度噪声在格点上放随机方向,比值噪声少了方块感;五次插值曲线让接缝更顺。
  • 噪声可以当角度、当距离场、当轮廓的半径,接上前面学过的一切。

练习:用噪声做一块大理石:先用噪声扭转空间,画出弯曲的条纹,再把条纹的颜色限制在白色和浅灰之间,最后用另一层更细的噪声加一点纹理。

  1. The Book of Shaders: Noise
  2. thebookofshaders.com/11

相关页面

图形学着色器伪随机Shader Pseudorandom片元着色器里没有真正的「每次调用都不同」的随机:rand(x) 必须是确定性伪随机——同一输入永远同一输出。常用手法是 fract(sin(x)大数);二维则用 dot 把 vec2 压成标量再喂给同一套路。把空间 floor 成格子后,格子共享一个随机值,就能在秩序与混沌之间做生成设计。图形学细胞噪声Cellular NoiseSteven Worley(1996)《A Cellular Texture Basis Function》——书中注明约在 Perlin 原始噪声发表十六年之后、其 Simplex Noise(2001)五年之前——把程序纹理建立在「到最近特征点的距离」上,即细胞噪声 / Worley 噪声。GPU 上用格子 + 邻域搜索代替「对全体点求最小距离」;再多存最近特征点身份即可得到 Voronoi 图。后续有 2×2 邻域加速、精确边界与「voro-noise」等变体。图形学分形布朗运动Fractional Brownian Motion分形布朗运动 (fBm) / 分形噪声:把多「八度 (octave)」噪声叠加——频率按空隙度 (lacunarity) 步进升高,振幅按增益 (gain) 衰减——得到跨尺度自相似细节。变体包括湍流(绝对值尖谷)、脊状噪声、乘性/多重分形,以及用噪声扭曲采样坐标的域扭曲 (domain warping)。图形学程序化图案Procedural Patterns片元着色器按像素执行:形状重复多少次,计算量几乎不变,因此特别适合平铺图案。核心手法是放大 st 再 fract 得到重复的 [0,1] 子空间,再在格子内画形、做矩阵变换,或按奇偶行错位、按格子旋转拼 Truchet。图形学着色器二维矩阵变换2D Matrix Transforms in Shaders在片元里「移动形状」其实是移动坐标系:平移加向量;旋转与缩放用 mat2 乘到坐标上。绕画布中心变换时,要先把空间挪到原点、变换、再挪回。矩阵乘法顺序有关;同一套矩阵也可拿来做 YUV↔RGB 等颜色空间线性变换。图形学二维 SDF 形状2D SDF Shapes在片元着色器里画形,不是发三角形图元,而是对每个像素问「我在形的里面还是外面」。矩形用 step/smoothstep 做轴对齐边界;圆与更复杂形用距离场 (distance field)——到特征点的距离再阈值。这是 2D SDF 程序化造型的入门路径。图形学着色器纹理采样Shader Texture SamplingGPU 把图像存成 sampler2D:一张浮点向量的「表」,坐标连续时硬件做插值。片元里用 texture2D(sampler, uv)(现代 GLSL 为 texture)按归一化 UV 取色。BoS 强调:通道是 [0,1] 连续域、可越界重复、可用噪声扭曲 UV——图像变成可拉伸的「数码软垫」,为后处理与光学玩具式特效铺路。图形学塑形函数Shaping Functions塑形函数把归一化到 [0,1] 的一维输入弯成可控制的曲线,是片元着色器里混合颜色、画边、做动画缓动的「基本功」。GLSL 的 pow/step/smoothstep/sin 等多为硬件加速;掌握一维再扩展到二维空间。游戏开发 · UE5Custom 材质表达式Custom Material ExpressionCustom 节点允许在材质图里直接写 HLSL:任意数量输入、指定输出类型,可选额外输出、define 与外部 .ush/.usf 包含路径。它是「内置节点做不到」时的逃生口;代价是无法常量折叠、输入仅函数局部可见、且必须是合法 HLSL。优先用内置节点与材质函数,只在必要时上 Custom。

被这些页面引用2 篇

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原