我将使用可工作的代码来解释Perlin噪声,而不依赖于其他解释。首先,您需要一种在2D点处生成伪随机浮点数的方法。每个点应该相对于其他点看起来是随机的,但诀窍在于相同的坐标应始终产生相同的浮点数。我们可以使用任何哈希函数来实现这一点 - 不仅仅是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);
}
我使用这个代码生成一个“景观”
landscape[i][j] = noise2(i,j);
(然后将其转换为图像),它总是产生相同的结果:
...
但是那看起来太随机了——像山丘和山谷过于密集。我们需要一种方法来“拉伸”每个随机点,例如在5个点上。对于这些“关键”点之间的值,您需要平滑渐变。
static float stretchedNoise2(float x_float, float y_float, float stretch) {
x_float /= stretch;
y_float /= stretch;
int x = (int) Math.floor(x_float);
int y = (int) Math.floor(y_float);
float fractional_X = x_float - x;
float fractional_Y = y_float - y;
double[] p = new double[4];
for (int j = 0; j < 4; j++) {
double[] p2 = new double[4];
for (int i = 0; i < 4; i++) {
p2[i] = noise2(x + i - 1, y + j - 1);
}
p[j] = cubicInterp(p2, fractional_X);
}
return (float) cubicInterp(p, fractional_Y);
}
public static double cubicInterp(double[] p, double x) {
return cubicInterp(p[0],p[1],p[2],p[3], x);
}
public static double cubicInterp(double v0, double v1, double v2, double v3, double x) {
double P = (v3 - v2) - (v0 - v1);
double Q = (v0 - v1) - P;
double R = v2 - v0;
double S = v1;
return P * x * x * x + Q * x * x + R * x + S;
}
如果您不理解细节,没关系-我不知道
Math.cos()
是如何实现的,但我仍然知道它是做什么的。而这个函数会给我们带来拉伸、平滑的噪声。
stretchedNoise2
函数以一定的比例(大或小)生成一个“景观”,即具有平稳坡度的随机点的景观。现在我们可以在彼此之上生成一系列景观:
public static double perlin2(float xx, float yy) {
double noise = 0;
noise += stretchedNoise2(xx, yy, 5) * 1;
noise += stretchedNoise2(xx, yy, 13) * 2;
return noise / (1+2); // make sure you sum the multipliers above
}
更准确地说,我们从每个样本中获取点数的加权平均值。
(
![noise2](https://istack.dev59.com/SSKIf.webp)
+ 2 *
![noise3](https://istack.dev59.com/1XgHp.webp)
)/ 3 =
![enter image description here](https://istack.dev59.com/giWUg.webp)
当您将一堆平滑噪声叠加在一起时,通常使用大约5个逐渐增加“拉伸”的样本,您会得到Perlin噪声。 (如果您理解上述句子,则理解Perlin噪声。)
还有其他实现方式,因为它们以不同的方式执行相同的操作而更快,但是因为现在不再是1983年,并且您正在开始编写景观生成器,所以您不需要了解所有特殊技巧和术语来理解Perlin噪声或进行有趣的事情。 例如:
1)
![noise](https://istack.dev59.com/msQKh.webp)
2)
![noise](https://istack.dev59.com/berbt.webp)
3)
float smearX = interpolatedNoise2(xx, yy, 99) * 99;
float smearY = interpolatedNoise2(xx, yy, 99) * 99;
ret += interpolatedNoise2(xx + smearX, yy + smearY, 13)*1;
float smearX2 = interpolatedNoise2(xx, yy, 9) * 19;
float smearY2 = interpolatedNoise2(xx, yy, 9) * 19;
ret += interpolatedNoise2(xx + smearX2, yy + smearY2, 13)*1;
ret += Math.cos( interpolatedNoise2(xx , yy , 5)*4) *1;