程序开发中总会用到随机方法,一般的随机方法虽然通用,但是产生的随机数又因为过于"随机",不适合用来生成平滑连续的随机数据(譬如自然地形的高度),这个时候我们便需要使用特殊的随机方法了, Perlin 噪声便是一种能够产生平滑(随机)数值的随机方法.
Value 噪声
为了更容易的理解 Perlin 噪声,我们先从较简单的 Value 噪声看起:
首先我们考虑 一维 情况(即通过一维坐标来获取随机值),如果我们仅使用一般随机方法的话,得到的随机数值是这样的:
可以看到数据杂乱无章,远不能说是平滑连续,有什么办法可以改进呢?
一种朴素的想法就是在整数坐标处仍然使用一般随机方法来生成随机值,但是对于处在(相邻)两个整数坐标之间的点(即浮点坐标点),则使用线性插值的方式(在这两个整数坐标点对应的随机值之间线性插值)来生成随机值,最后得到的随机数值是这样的:
可以看到数据比起之前已经平滑连续了不少,但是在整数坐标处仍然不够平滑连续(整数坐标处不可导(自然也不连续)),而这是由于我们之前采用了线性插值的方式来生成随机值造成的,改善的方式也比较明晰,就是改用非线性插值的方式来生成整数坐标间的随机值.
当然,也不是随便一种非线性插值都可以满足我们的要求(我们需要该非线性插值在(相邻)端点(整数坐标)处可导并且导数相同(连续)),使用下面的非线性插值方法可以达到我们的目标(公式中的 t t t 是原始的线性插值系数, a a a 和 b b b 则是两个端点处的随机值( a a a 对应左端点, b b b 对应右端点), r r r 则是最终的噪声值):
t ′ = 3 t 2 − 2 t 3 r = ( 1 − t ′ ) a + t ′ b t' = 3t^2 - 2t^3 \\ r = (1 - t')a + t'b t′=3t2−2t3r=(1−t′)a+t′b
使用上述的方法,我们得到的随机数值是这样的:
上述的非线性插值公式还可以进一步改进,基本思想就是使(相邻)端点(整数坐标)处更加"平滑连续"(即在(相邻)端点(整数坐标)处二阶导数相同(连续)):
t ′ = 6 t 5 − 15 t 4 + 10 t 3 r = ( 1 − t ′ ) a + t ′ b t' = 6t^5 - 15t^4 + 10t^3 \\ r = (1 - t')a + t'b t′=6t5−15t4+10t3r=(1−t′)a+t′b
基于改进的公式,我们得到的随机数值是这样的:
至此,我们便得到了 一维 的 Value 噪声.
那么 二维(即通过二维坐标来获取随机值) Value 噪声又如何生成呢 ?
其实思路上 二维 Value 噪声 和一维 Value 噪声也是一致的,仍然是首先在端点坐标处生成随机值,之后基于给定的坐标进行非线性插值.
当然,由于坐标是二维的关系,端点已经不是一维情况下坐标轴上的整数坐标了,而是变成了正方形的四个顶点(我们使用正方形平铺二维平面,这样任意一个二维坐标都可对应到单个正方形,而这个正方形的四个顶点即是我们用来非线性插值的端点)
当然,由于二维坐标的关系,插值的时候我们需要先在 x x x 轴上分别做两次插值,再在 y y y 轴上做一次插值( a , b , c , d a, b, c, d a,b,c,d 为四个端点的随机值, u u u 为 x x x 轴原始的线性插值系数, v v v 为 y y y 轴原始的线性插值系数):
u ′ = 6 u 5 − 15 u 4 + 10 u 3 v ′ = 6 v 5 − 15 v 4 + 10 v 3 e = ( 1 − u ′ ) a + u ′ b f = ( 1 − u ′ ) c + u ′ d r = ( 1 − v ′ ) e + v ′ f u' = 6u^5 - 15u^4 + 10u^3 \\ v' = 6v^5 - 15v^4 + 10v^3 \\ e = (1 - u')a + u'b \\ f = (1 - u')c + u'd \\ r = (1 - v')e + v'f u′=6u5−15u4+10u3v′=6v5−15v4+10v3e=(1−u′)a+u′bf=(1−u′)c+u′dr=(1−v′)e+v′f
下面是二维 Value 噪声的示意图(其中各个坐标点的明暗程度代表了相应的随机值):
(注:上图展示的是实际生成的二维 Value 噪声数据,显示上没有做额外的插值处理,所以看起来会有明显的边界)
至此,我们也明白了 二维 Value 噪声的生成原理.
未完待续