有多种方法可以实现相同的算法,有些比其他更快或更慢,有些更容易或更难理解。 The original implementation by Ken Perlin 光看就很难理解。所以你链接的一些文章(包括我写的#2,耶!),尝试简化实现以使其更容易理解。
但最终,它的算法完全一样:
- 取输入,计算包含输入点的正方形 4 个角的坐标(对于 2D Perlin 噪声,如果使用 3D 版本,则为立方体)
- 计算所有 4 个的随机值(首先为每个分配一个随机梯度向量(在 2D 中有 4 种可能性:(+1, +1), (-1, +1), (-1 , -1) 和 (+1, -1)),然后计算这个随机梯度向量与从正方形角到输入点的向量的点积)
- 最后,在这 4 个随机值之间进行平滑插值以获得最终值
在文章 #1 中,grad 函数直接返回点积,而在文章 #2 中,创建向量对象并调用点积函数以明确正在执行的操作(这可能会慢一些与其他实现相比,因为每次您想要运行算法时都会创建和使用大量矢量对象)。
两种实现是否会生成相同的地形/高度图取决于它们是否为正方形/立方体的每个角生成相同的随机值(点积的结果)。如果两种算法为网格上的每个整数点(所有可能的正方形/立方体的所有角)生成相同的随机值,那么它们将产生相同的结果。 Ken Perlin 的原始实现和 3 篇文章都使用整数数组为每个角(4 种可能的选择)生成一个随机梯度向量来计算点积。所以理论上如果数组是相同的,那么它们应该产生相同的结果。 (除非某些实现使用另一种方法来生成随机向量。)
我不确定这是否能回答你的问题,所以请不要犹豫,问其他问题:)
编辑:
通常,您不会单独使用 Perlin 噪声。因此,对于您想要的每个最终值(例如高度图纹理中的单个像素),您将多次调用噪声函数(八度音阶)。例如:
float finalValue = 0.0f;
float amplitude = 1.0f;
float frequency = 1.0f;
int octaveCount = 8;
for (int octave = 0; octave < octaveCount; ++octave) {
finalValue += amplitude * noise(x * frequency, y * frequency, z * frequency);
amplitude *= 0.5f;
frequency *= 2.0f;
}
// Do something fun with 'finalValue'
频率、幅度和八度音阶数是您可以用来产生不同值的最常用参数。
例如,如果您要生成地形,则需要多个八度音阶。第一个将产生山的粗略形状,因此您需要高振幅(示例代码中为 1.0)和低频(上述代码中也是 1.0)。但是只有这个八度音程会导致非常平滑的地形,没有细节。对于那些小细节,您需要更多的八度音阶,但频率更高(因此在相同的输入范围内(x、y、z),Perlin 噪声值会有更多的起伏),并且更低幅度(你想要一些小细节,因为如果你保持与第一个八度音阶相同的幅度(在示例代码中为 1.0),那么会有很多起伏非常接近并且非常高,这将导致非常崎岖的山(想象一下每走几米就有 100 米 80 度的坡度和斜坡))
您可以使用这些参数来获得不同的结果。您还可以查找称为“域扭曲”或“扭曲噪声”的东西。基本上,您将噪声函数称为噪声函数的输入。喜欢而不是打电话:
float result = noise(x, y, z);
你会这样称呼:
// The numbers used are arbitrary values, you can just play around until you get something cool
float result = noise(noise(x * 1.7), 0.5 * noise(y * 4.1), noise(z * 2.3));
这可以产生really interesting results