If you're seeing this message, it means we're having trouble loading external resources on our website.

如果你被网页过滤器挡住,请确保域名*.kastatic.org*.kasandbox.org 没有被阻止.

主要内容

珀林噪音

一个好的随机数生成器生成的数字之间没有联系、也看不出什么模式。正如我们开始看到的,在对系统的、符合现实的行为进行编程时,一点随机性可能是一件好事。然而,将随机性作为单一的指导原则却不符合自然规则。
有一种能产生更自然的结果的算法,就是常说的 "柏林噪声"。在20世纪 80年代初,Ken Perlin 在拍摄原创电影时开发了噪音功能;在利用计算机生成特效时,他用这种方法的来创建程序的质感。1997年,Perlin 因这项工作的技术成就获得了奥斯卡奖。柏林噪声可用于生成具有符合自然特点的各种效果,比如云、景观和大理石等图案纹理。
柏林噪声看起来也更自然形象,因为它生成的是一系列自然有序 ("平滑") 的伪随机数。下图显示了随时间变化的 Perlin 噪声,其中x 轴表示时间;注意观察曲线的平滑度。
Nature of Code 图
图 I.5:噪声
与之相对照的,下图表示的是随时间变化的纯粹随机数。
Nature of Code 图
图 I.6: 随机数
ProcessingJS 有一个 Perlin 噪声算法的内置程序:函数 noise()noise() 函数可以设定一个、两个或三个参数,以便分别用于计算一维、二维或三维度噪声。我们从一维噪声开始,看看具体情形吧。

噪声详情

噪声参考资料 告诉我们,噪音的音域跨越若干个 “八度。” 调用 noiseDetail() 函数既会改变声音的音域,也会改变声音之间的高低对比。通过这种方式反过来改变了噪声函数的结果。
假设我们在 processingJS 窗口随机选择一个 x 位置绘制一个圆:
var x = random(0, width);
ellipse(x, 180, 16, 16);
现在,我们想要的不是随机的 x 位置,而是将圆画在 "更顺畅" 的 Perlin 噪声 x 位置。你可能会认为你需要做的只是把random() 替换成 noise(),程序变成如下:
var x = noise(0, width);
ellipse(x, 180, 16, 16);
虽然从概念上讲,这正是我们要做的——根据 Perlin 噪声计算出介于0和宽度之间的 x 值——但在实际编程是这样做是错误的。虽然random() 函数的参数可以在最小值和最大值之间取值, 但noise() 函数取值不是这样的。相反,我们在noise()函数中输入的参数表示的是 "时间时刻" ,是一个始终介于0和1之间的值。 我们可以把一维的 Perlin 噪声看作是一个随着时间变化的一系列线性值。下面是一个输入和返回值的示例:
时间噪声值
00.469
0.010.480
0.020.492
0.030.505
0.040.517
为了访问 processingJS 中的一个噪声值,我们要将特定的时间时刻传递给 noise() 函数。例如:
var n = noise(0.03);
根据上表,noise()函数在时间值为0.03 时,返回0.505的噪声值。我们可以编写一个程序,存储一个时间变量,并在draw()函数中连续请求噪声值作为其输入值。
上述代码会导致反复输出相同的值。之所以会出现这种情况,是因为我们在同一个时间点上,反复询问 noise() 函数的结果。
但是,如果我们将时间变量t不断增大, 我们得到的结果却不同。
我们增加t 的变动速率也会影响噪声的平滑度。如果我们给定的时间变化间隔大, 那么数值就会跳跃性强,得出的值将更加随机。
随着时间变化的噪声
图 1.7
尝试运行几次上面的代码,将时间变化间隔分别设为 0.01,0.02,0.05,0.1,0.0001,你会看到不同的结果。

映射噪声

现在我们看看可以怎么使用噪声值的问题了。一旦我们有了范围在0到1之间的噪声值, 我们就可以将它用于映射出我们想要的范围了。
我们可以乘以范围内的最大数字,但更好的方式是新学习使用 processingJS 的map() 函数,我们在以后更多的情况下会经常用到它。map() 函数有五个参数。首先是我们要映射的值,就是上面的n。然后,我们必须给出它的取值的当前范围(最小值和最大值),接着是给出我们想要的范围。
Nature of Code 图
图 I.8
在这种情况下,虽然我们知道噪声的范围在0和1之间,但我们希望绘制一个宽度介于0和当前宽度之间的矩形。
我们可以将相同的逻辑应用到随机漫步上,并用 Perlin 噪声方法为位置分配其 x 和 y 值。
请注意上面的示例,这里需要一对额外变量:txty 。这是因为我们需要跟踪两个时间变量, 一个用于Walker对象的 x 轴位置,另一个用于 y 轴位置。
但这些变量有些奇怪。 为什么 tx 从0开始,ty 从 10, 000 开始?尽管这些数字是主观的选择,但我们非常具体地设置了具有不同值的两个初始时间变量。这是因为噪声函数是确定性的: 对于一个特定时间t ,它每次都为您返回相同的结果。如果我们在时间 t 点,同时要求获取 xy的噪声值,那么 xy 会始终相等,这意味着 Walker 对象只会沿对角线移动。相反,我们只是使用噪声空间的两个不同部分,让 x 从0开始,y 从 10, 000 开始,这样xy 就可以相互独立地进行绘制。
Nature of Code 图
图 I.9
事实上,这里并没有实际的时间概念。我们用时间只是比喻,帮助我们理解解噪声函数是如何运作的,但我们真正拥有的是空间,而不是时间。上图描述了一维空间中的一系列线性噪声值,我们可以随时在特定的 x 轴位置获取数值。在实例中,您经常会看到一个名为xoff 的变量,它表示在噪声图上的 x 偏移量,而不是时间的变化 t(如图中所示)。
在接下来的挑战中,你会尝试用稍有不同的方式将噪声与 Walker结合起来。好好享受编程乐趣!

“自然模拟”系列课程是由 Daniel Shiffman 的 "编程的本质" 衍生而来,基于 知识共享 著名-非商用性 3.0 本地化许可协议

想加入讨论吗?

尚无帖子。
你会英语吗?单击此处查看更多可汗学院英文版的讨论.