Math 用于地形生成的柏林噪声

Math 用于地形生成的柏林噪声,math,perlin-noise,Math,Perlin Noise,我正在尝试实现2D柏林噪声,以创建类似于雷击艇的地形(雷击艇实际上不使用2D柏林噪声),而没有悬垂物或洞穴等 我的做法是创建一个[50][20][50]立方体数组,其中[20]是数组的最大高度,其值将由柏林噪声确定。然后我将用多维数据集数组填充该数组 我一直在读,但我不明白,我如何计算4梯度向量,并在我的代码中使用它?是否每个相邻的2D数组(如[2][3]和[2][4])都有不同的4梯度向量 此外,我还了解到,通用的柏林噪声函数也采用一个将用作种子的数值,在这种情况下,我将把它放在哪里?我将使用

我正在尝试实现2D柏林噪声,以创建类似于雷击艇的地形(雷击艇实际上不使用2D柏林噪声),而没有悬垂物或洞穴等

我的做法是创建一个[50][20][50]立方体数组,其中[20]是数组的最大高度,其值将由柏林噪声确定。然后我将用多维数据集数组填充该数组

我一直在读,但我不明白,我如何计算4梯度向量,并在我的代码中使用它?是否每个相邻的2D数组(如[2][3]和[2][4])都有不同的4梯度向量


此外,我还了解到,通用的柏林噪声函数也采用一个将用作种子的数值,在这种情况下,我将把它放在哪里?

我将使用工作代码解释柏林噪声,而不依赖其他解释。首先,需要一种在二维点生成伪随机浮点的方法。每个点相对于其他点看起来应该是随机的,但关键是相同的坐标应该总是产生相同的浮动。我们可以使用任何散列函数来实现这一点,而不仅仅是Ken Perlin在代码中使用的散列函数。这里有一个:

static float noise2(int x, int y) {
    int n = x + y * 57;
    n = (n << 13) ^ n;
    return (float) (1.0-((n*(n*n*15731+789221)+1376312589)&0x7fffffff)/1073741824.0);
}
如果您不了解细节,那没关系-我不知道如何实现
Math.cos()
,但我仍然知道它的功能。这个函数给了我们拉伸,平滑的噪音

->

stretchedNoise2
函数以一定的比例(大或小)生成一个“景观”——一个由随机点组成的景观,这些点之间有平滑的坡度。现在,我们可以在彼此的顶部生成一系列风景:

public static double perlin2(float xx, float yy) {
    double noise = 0;
    noise += stretchedNoise2(xx, yy,  5) * 1; // sample 1
    noise += stretchedNoise2(xx, yy, 13) * 2; // twice as influential

    // you can keep repeating different variants of the above lines
    // some interesting variants are included below.

    return noise / (1+2); // make sure you sum the multipliers above
}
为了更准确地说,我们得到了每个样本点的加权平均值

(+2*)/3=

当你把一堆平滑的噪声叠加在一起,通常是5个增加“拉伸”的样本,你会得到柏林噪声。(如果你理解最后一句话,你就理解柏林噪音。)

还有其他更快的实现,因为它们以不同的方式做相同的事情,但因为现在已经不是1983年了,而且因为您正在开始编写一个景观生成器,您不需要了解他们用来理解柏林噪声或用它做有趣事情的所有特殊技巧和术语。例如:

1) 2)3)


关于柏林噪声

Perlin噪波是为了生成随机的连续曲面(实际上是程序纹理)。它的主要特点是噪声在空间上总是连续的

从文章中:

柏林噪声是在空间上产生相干噪声的函数。相干噪波意味着,对于空间中的任意两点,噪波函数的值在从一点移动到另一点时会平滑变化,也就是说,不存在间断

简单地说,柏林噪声如下所示:

 _         _    __
   \    __/ \__/  \__
    \__/
但这肯定不是柏林噪声,因为存在间隙:

 _         _ 
  \_    __/  
    ___/    __/
计算噪音(或破碎梯度!)

正如@markspace所说,柏林噪声在数学上是困难的。让我们通过生成1D噪波来简化

想象一下以下1D空间:

________________
首先,我们定义一个网格(或一维空间中的点):

然后,我们为每个网格点随机选择一个噪波值(该值相当于2D噪波中的梯度):

现在,计算网格点的噪波值很容易,只需选择值:

noise(3) => 0.5
但任意点
p
的噪声值需要根据最近的网格点
p1
p2
使用其值和影响进行计算:

// in 1D the influence is just the distance between the points
noise(p)   => noise(p1) * influence(p1) + noise(p2) * influence(p2)
noise(2.5) => noise(2)  * influence(2, 2.5) + noise(3) * influence(3, 2.5)
           => 0 * 0.5 + 0.5 * 0.5 => 0.25
结束!现在我们可以计算1D噪声,只需为2D添加一个维度即可。:-)

希望它能帮助你理解!现在请阅读工作代码并发出愉快的声音

编辑:

评论中的后续问题:


我在维基百科的文章中读到,2d柏林墙中的梯度向量应该是1的长度(单位圆)和随机方向。既然向量有X和Y,我该怎么做呢

这可以很容易地从中提起和调整。在下面找到一个伪代码

gradient.x = random()*2 - 1;
gradient.y = random()*2 - 1;
normalize_2d( gradient );
其中
normalize_2d
为:

// normalizes a 2d vector
function normalize_2d(v)
   size = square_root( v.x * v.x + v.y * v.y );
   v.x = v.x / size;
   v.y = v.y / size;

计算坐标x,y处的柏林噪声

function perlin(float x, float y) {

    // Determine grid cell coordinates
    int x0 = (x > 0.0 ? (int)x : (int)x - 1);
    int x1 = x0 + 1;
    int y0 = (y > 0.0 ? (int)y : (int)y - 1);
    int y1 = y0 + 1;

    // Determine interpolation weights
    // Could also use higher order polynomial/s-curve here
    float sx = x - (double)x0;
    float sy = y - (double)y0;

    // Interpolate between grid point gradients
    float n0, n1, ix0, ix1, value;
    n0 = dotGridGradient(x0, y0, x, y);
    n1 = dotGridGradient(x1, y0, x, y);
    ix0 = lerp(n0, n1, sx);
    n0 = dotGridGradient(x0, y1, x, y);
    n1 = dotGridGradient(x1, y1, x, y);
    ix1 = lerp(n0, n1, sx);
    value = lerp(ix0, ix1, sy);

    return value;
}

从数学上讲,真正的柏林噪声是相当困难的。()@markspace那是单纯的噪音。如果你想要柏林噪声,试试看,这是他自己写的。我在维基百科的文章中读到,2d柏林的梯度向量应该是1(单位圆)的长度和随机方向。由于矢量有X和Y,我该如何准确地做到这一点?注意:我删除了一些注释,因为它们与2D perling噪声无关(但与1D perling噪声有关),并且对后续问题没有建设性意义。我更新了答案,以包括如何生成具有随机方向的规范化矢量(大小1)。@Filipee Borges感谢您的回答。有一件事我仍然不明白,在大多数柏林噪声库中,(我在《处理与统一》中看到),柏林噪声函数取一个种子数,因此结果不同,但对于相同的种子数,结果总是相同的(在我的问题中的文章中,对于相同的X和Y,4梯度也总是相同的),在这些数学步骤中,种子数在哪里使用?种子数是伪随机生成器的种子@请看:对不起,我真的不理解你:/你描述的第一步,实际上是维基百科文章中的哪一步?我可以解释柏林噪音,但我不能解释其他人的解释。不过,其中很多都是不必要的混淆。如果你不理解上面的内容,请告诉我。我做了一些编辑,并添加了一些可能有帮助的图片。
// in 1D the influence is just the distance between the points
noise(p)   => noise(p1) * influence(p1) + noise(p2) * influence(p2)
noise(2.5) => noise(2)  * influence(2, 2.5) + noise(3) * influence(3, 2.5)
           => 0 * 0.5 + 0.5 * 0.5 => 0.25
gradient.x = random()*2 - 1;
gradient.y = random()*2 - 1;
normalize_2d( gradient );
// normalizes a 2d vector
function normalize_2d(v)
   size = square_root( v.x * v.x + v.y * v.y );
   v.x = v.x / size;
   v.y = v.y / size;
function perlin(float x, float y) {

    // Determine grid cell coordinates
    int x0 = (x > 0.0 ? (int)x : (int)x - 1);
    int x1 = x0 + 1;
    int y0 = (y > 0.0 ? (int)y : (int)y - 1);
    int y1 = y0 + 1;

    // Determine interpolation weights
    // Could also use higher order polynomial/s-curve here
    float sx = x - (double)x0;
    float sy = y - (double)y0;

    // Interpolate between grid point gradients
    float n0, n1, ix0, ix1, value;
    n0 = dotGridGradient(x0, y0, x, y);
    n1 = dotGridGradient(x1, y0, x, y);
    ix0 = lerp(n0, n1, sx);
    n0 = dotGridGradient(x0, y1, x, y);
    n1 = dotGridGradient(x1, y1, x, y);
    ix1 = lerp(n0, n1, sx);
    value = lerp(ix0, ix1, sy);

    return value;
}