柏林噪声—将黑白像素变成山河湖泊

通俗地了解游戏中的随机地形是怎么生成的

Featured image

柏林噪声有什么用

柏林噪声是一种自然噪声生成算法,可以用于生成一些自然的随机纹理,广泛用于游戏、建模等领域。在凸凹贴图它能很好地模拟火焰、云彩、奇形怪状的岩石,以及树木和大理石表面等。

柏林噪声算法是什么样的

我们不妨把一个二维平面上每个点赋予一个值,以这个值作为每个格点的”高度“,我们就能据此得到一个三维的地形。然而,每个格点的值如何确定才能使得整个地形平滑流畅,而又是随机的呢?柏林噪声就可以解决这个问题。

首先,我们先从白噪声开始

白噪声

白噪声是最简单的噪声,每个点都随机赋予一个值,但如果游戏中出现这样的地形,玩家肯定会疯掉的,我们用灰度图来更直观地感受一下白噪声。

值噪声

首先,我们把整个世界分为很多区块,如一个个16x16的正方形,我们给每个区块的四个格点上分别赋予一个伪随机数,用伪随机数是为了使得给这个世界一个固定的种子时,生成的世界都是一样的。

不妨取一个16x16的区块,把区块四个角的值设为 $a,\ b,\ c,\ d$ ,那么值噪声算法就可以通过这四个值将该区块内每个点的噪声值算出来

对于要求的位置为 $(x,\ y)$ 的格子的值 $w$ , 我们首先要求 $w_1(x,\ 16)$ 和 $w_2(x,\ 0)$, 我们用加权平均算法

\[w_1 =\cfrac{a(16-x)+bx}{16}\] \[w_2 =\cfrac{d(16-x)+cx}{16}\]

然后我们用一样的算法算出 $w$

\[w=\cfrac{w_1y+w_2(16-y)}{16}\]

这样我们就能确定每一个点的噪声值了, 最后将噪声值转化为高度即可, 对于每个区块, 我们都可以用这种算法

以下是一张在Minecraft中用值噪声算法生成的地形

我们发现这样的地形细节太少了, 而且地形不够丝滑, 我们可以考虑修改加权的方式

原来的加权时的权值是直线 $y=x$ , 我们可以用一个更平滑的, 过$(0,\ 0),\ (1,\ 1)$ 的曲线来代替它, 如正余弦函数, 抛物线等, 使得插值更加平滑

\[f(x)=x^2\]

以算 $w$ 为例子

\[w=w_1\cdot f(\cfrac{y}{16})+w_2\cdot f(\cfrac{16-y}{16})\]

这样会平滑许多, 但是值噪声还是有些单调, 不符合我们的要求, 那么我们就需要柏林噪声

柏林噪声

我们首先同样需要根据种子生成一串伪随机数, 接着我们给每个区块的顶点一个一样长的初始向量, 用伪随机数列赋予向量一个角度, 我们一般按照从某个区块螺旋向外的顺序赋予角度, 因为这符合玩家探索世界的方式.

对于一个区块, 每个顶点我们都生成好了一个梯度向量, 不妨设为 $\vec{a},\ \vec{b},\ \vec{c},\ \vec{d}$

接着, 对于区块内的某个点, 我们再给它四个向量, 分别为这个点指向四个顶点, 记为 $\vec{a’},\ \vec{b’},\ \vec{c’},\ \vec{d’}$

接着我们进行向量点积运算

\[a=\vec{a}\cdot\vec{a'}\] \[b=\vec{b}\cdot\vec{b'}\] \[c=\vec{c}\cdot\vec{c'}\] \[d=\vec{d}\cdot\vec{d'}\]

取2柏林函数为插值用的函数

\[f(x)=6x^5-15x^4+10x^3\]

设区块边长为 $l$ , 最终我们可以得出

\[w_{x,y}=f(\cfrac{l-x}{l})f(\cfrac{y}{l})a+f(\cfrac{x}{l})f(\cfrac{y}{l})b+f(\cfrac{x}{l})f(\cfrac{l-y}{l})c+f(\cfrac{l-x}{l})f(\cfrac{l-y}{l})d\]

这就是柏林噪声

如果你觉得细节还是不够, 可以多加几层柏林噪声, 即分形柏林噪声

分形柏林噪声

例如, 我总共创建4层柏林噪声, 每层密度是前一层的两倍

我们希望每一层的影响逐渐减小, 有点类似一个收敛的级数, 不然最后一个高密度噪声将覆盖前面的噪声, 又将回归白噪声

影响力的衰减依赖一个振幅衰减的乘数来控制, 如每一层的影响力是前一层的一半, 振幅衰减系数为0.5

图层得叠加也非常简单, 把每个像素的值相加即可

最后, 我们通过一些数学计算得出最后每个像素的高度的范围, 可以通过映射噪声值到颜色上来生成地形

你升职可以生成三维噪声, 只要给每个立体区块的8个顶点都赋予梯度向量, 类比之前计算出每个像素块的值

参考

https://www.bilibili.com/video/BV1vx4y1i7gn/

https://www.bilibili.com/video/BV1Hh411s7ir/