接下来的地形生成代码会接触到噪声函数,所以本篇内容就先逆向MC的噪声函数吧
噪声函数就是比较好看的随机数,如果用纯随机数(白噪声)不适合生成地形,但是噪声函数就很适合
具体的噪声介绍可以参考这篇文章,建议先阅读一下,否则后面的算法可能看不懂…
各种NoiseGenerator
在包net.minecraft.world.gen
里可以看到几个噪声生成器,它们的命名比较乱所以不要看类名来推断算法(本来柏林噪声的命名就经常被混淆),其实MC里只有Perlin噪声和Simplex噪声
NoiseGeneratorImproved
用来生成一个柏林噪声
public class NoiseGeneratorImproved extends NoiseGenerator
{
// 0~255的随机排列
private int[] permutations;
// 座标偏移量
public double xCoord;
public double yCoord;
public double zCoord;
// 三维梯度向量,去掉y维度后和二维的一样
private static final double[] field_152381_e = new double[] {1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 0.0D, -1.0D, 0.0D};
private static final double[] field_152382_f = new double[] {1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D};
private static final double[] field_152383_g = new double[] {0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, -1.0D, -1.0D, 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 1.0D, 0.0D, -1.0D};
// 二维梯度向量
private static final double[] field_152384_h = new double[] {1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 1.0D, -1.0D, 0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 0.0D, -1.0D, 0.0D};
private static final double[] field_152385_i = new double[] {0.0D, 0.0D, 0.0D, 0.0D, 1.0D, 1.0D, -1.0D, -1.0D, 1.0D, 1.0D, -1.0D, -1.0D, 0.0D, 1.0D, 0.0D, -1.0D};
public NoiseGeneratorImproved()
{
this(new Random());
}
public NoiseGeneratorImproved(Random rand)
{
this.permutations = new int[512];
this.xCoord = rand.nextDouble() * 256.0D;
this.yCoord = rand.nextDouble() * 256.0D;
this.zCoord = rand.nextDouble() * 256.0D;
// 生成0~255的随机排列
for (int i = 0; i < 256; ++i)
{
this.permutations[i] = i;
}
for (int i = 0; i < 256; ++i)
{
// 从i~255中选j
int j = rand.nextInt(256 - i) + i;
// 把i和j交换
int t = this.permutations[i];
this.permutations[i] = this.permutations[j];
this.permutations[j] = t;
// 形成一个周期
this.permutations[i + 256] = this.permutations[i];
}
}
// 在a与b间线性插值
public final double lerp(double t, double a, double b)
{
return a + t * (b - a);
}
// 返回与二维梯度向量点乘的结果,参数:梯度向量索引, 向量(其实就是权重)
public final double func_76309_a(int index, double xWeight, double zWeight)
{
int i = index % 16;
return field_152384_h[i] * xWeight
+ field_152385_i[i] * zWeight;
}
// 返回与三维梯度向量点乘的结果,参数:梯度向量索引, 向量(其实就是权重)
public final double grad(int index, double xWeight, double yWeight, double zWeight)
{
int i = index % 16;
return field_152381_e[i] * xWeight
+ field_152382_f[i] * yWeight
+ field_152383_g[i] * zWeight;
}
/**
* pars: noiseArray , xOffset , yOffset , zOffset , xSize , ySize , zSize , xScale, yScale , zScale , noiseScale.
* noiseArray should be xSize*ySize*zSize in size
*/
// 这个noiseScale和振幅成反比...
public void populateNoiseArray(double[] result, double xOffset, double yOffset, double zOffset, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale, double noiseScale)
{
if (ySize == 1) // 二维
{
int resultIndex = 0;
// 结果缩放系数,和noiseScale成反比
double noiseRatio = 1.0D / noiseScale;
for (int _x = 0; _x < xSize; ++_x)
{
// 经过偏移和缩放的x
double x = xOffset + (double)_x * xScale + this.xCoord;
// 不大于x的最大整数
int xFloor = (int)x;
if (x < (double)xFloor)
{
--xFloor;
}
int xIndex = xFloor % 256;
// 此时x为晶格内的座标[0, 1)
x = x - (double)xFloor;
// 缓和曲线s(t) = 6t^5 - 15t^4 + 10t^3
double sx = x * x * x * (x * (x * 6.0D - 15.0D) + 10.0D);
for (int _z = 0; _z < zSize; ++_z)
{
// 经过偏移和缩放的z
double z = zOffset + (double)_z * zScale + this.zCoord;
// 不大于z的最大整数
int zFloor = (int)z;
if (z < (double)zFloor)
{
--zFloor;
}
int zIndex = zFloor % 256;
// 此时z为晶格内的座标[0, 1)
z = z - (double)zFloor;
// 缓和曲线s(t) = 6t^5 - 15t^4 + 10t^3
double sz = z * z * z * (z * (z * 6.0D - 15.0D) + 10.0D);
// 取梯度向量G=G[(i+P[j])mod n]
// 左上角梯度向量索引
int vecIndex1 = this.permutations[this.permutations[xIndex]] + zIndex;
// 右上角梯度向量索引
int vecIndex2 = this.permutations[this.permutations[xIndex + 1]] + zIndex;
// x方向上点乘(加权),结果用缓和曲线插值
double xRes1 = this.lerp(sx,
this.func_76309_a(this.permutations[vecIndex1], x , z),
this.grad( this.permutations[vecIndex2], -(1.0D - x), 0.0D, z));
double xRes2 = this.lerp(sx,
this.grad(this.permutations[vecIndex1 + 1], x , 0.0D, -(1.0D - z)),
this.grad(this.permutations[vecIndex2 + 1], -(1.0D - x), 0.0D, -(1.0D - z)));
// 两个x方向的结果在z方向用缓和曲线插值
double res = this.lerp(sz, xRes1, xRes2);
result[resultIndex++] += res * noiseRatio;
}
}
}
else // 三维,参考上面,懒得写注释和反混淆了...
{
int resultIndex = 0;
double noiseRatio = 1.0D / noiseScale;
int lastYIndex = -1;
for (int _x = 0; _x < xSize; ++_x)
{
double x = xOffset + (double)_x * xScale + this.xCoord;
int xFloor = (int)x;
if (x < (double)xFloor)
{
--xFloor;
}
int xIndex = xFloor % 256;
x = x - (double)xFloor;
double sx = x * x * x * (x * (x * 6.0D - 15.0D) + 10.0D);
for (int _z = 0; _z < zSize; ++_z)
{
double z = zOffset + (double)_z * zScale + this.zCoord;
int zFloor = (int)z;
if (z < (double)zFloor)
{
--zFloor;
}
int zIndex = zFloor % 256;
z = z - (double)zFloor;
double sz = z * z * z * (z * (z * 6.0D - 15.0D) + 10.0D);
double xRes1, xRes2, xRes3, xRes4;
for (int _y = 0; _y < ySize; ++_y)
{
double y = yOffset + (double)_y * yScale + this.yCoord;
int yFloor = (int)y;
if (y < (double)yFloor)
{
--yFloor;
}
int yIndex = yFloor % 256;
y = y - (double)yFloor;
double sy = y * y * y * (y * (y * 6.0D - 15.0D) + 10.0D);
if (_y == 0 || yIndex != lastYIndex)
{
lastYIndex = yIndex;
int l = this.permutations[xIndex] + yIndex;
int i1 = this.permutations[l] + zIndex;
int j1 = this.permutations[l + 1] + zIndex;
int k1 = this.permutations[xIndex + 1] + yIndex;
int l1 = this.permutations[k1] + zIndex;
int i2 = this.permutations[k1 + 1] + zIndex;
xRes1 = this.lerp(sx, this.grad(this.permutations[i1], x, y, z), this.grad(this.permutations[l1], x - 1.0D, y, z));
xRes2 = this.lerp(sx, this.grad(this.permutations[j1], x, y - 1.0D, z), this.grad(this.permutations[i2], x - 1.0D, y - 1.0D, z));
xRes3 = this.lerp(sx, this.grad(this.permutations[i1 + 1], x, y, z - 1.0D), this.grad(this.permutations[l1 + 1], x - 1.0D, y, z - 1.0D));
xRes4 = this.lerp(sx, this.grad(this.permutations[j1 + 1], x, y - 1.0D, z - 1.0D), this.grad(this.permutations[i2 + 1], x - 1.0D, y - 1.0D, z - 1.0D));
}
double yRes1 = this.lerp(sy, xRes1, xRes2);
double yRes2 = this.lerp(sy, xRes3, xRes4);
double res = this.lerp(sz, yRes1, yRes2);
result[resultIndex++] += res * noiseRatio;
}
}
}
}
}
}
NoiseGeneratorOctaves
用来把多个NoiseGeneratorImproved分形叠加
public class NoiseGeneratorOctaves extends NoiseGenerator
{
/** Collection of noise generation functions. Output is combined to produce different octaves of noise. */
private NoiseGeneratorImproved[] generatorCollection;
private int octaves;
public NoiseGeneratorOctaves(Random rand, int octaveCount)
{
this.octaves = octaveCount;
this.generatorCollection = new NoiseGeneratorImproved[octaveCount];
for (int i = 0; i < octaveCount; ++i)
{
this.generatorCollection[i] = new NoiseGeneratorImproved(rand);
}
}
/**
* pars:(par2,3,4=noiseOffset ; so that adjacent noise segments connect) (pars5,6,7=x,y,zArraySize),(pars8,10,12 =
* x,y,z noiseScale)
*/
public double[] generateNoiseOctaves(double[] result, int x, int y, int z, int xArraySize, int yArraySize, int zArraySize, double xScale, double yScale, double zScale)
{
if (result == null)
{
result = new double[xArraySize * yArraySize * zArraySize];
}
else
{
// 结果清0,因为后面会叠加
for (int i = 0; i < result.length; ++i)
{
result[i] = 0.0D;
}
}
// 控制倍频函数的频率、幅度
double scale = 1.0D;
// 遍历倍频函数
for (int i = 0; i < this.octaves; ++i)
{
double x2 = (double)x * scale * xScale;
double y2 = (double)y * scale * yScale;
double z2 = (double)z * scale * zScale;
// 这部分大概是防止溢出的
// x2Floor为不大于x2的最大整数
long x2Floor = MathHelper.floor_double_long(x2);
long z2Floor = MathHelper.floor_double_long(z2);
x2 = x2 - (double)x2Floor;
z2 = z2 - (double)z2Floor;
x2Floor = x2Floor % 0x1000000L;
z2Floor = z2Floor % 0x1000000L;
x2 = x2 + (double)x2Floor;
z2 = z2 + (double)z2Floor;
// 叠加
this.generatorCollection[i].populateNoiseArray(result, x2, y2, z2, xArraySize, yArraySize, zArraySize, xScale * scale, yScale * scale, zScale * scale, scale);
// 频率减半,幅度加倍
scale /= 2.0D;
}
return result;
}
/**
* Bouncer function to the main one with some default arguments.
*/
public double[] generateNoiseOctaves(double[] result, int x, int z, int xArraySize, int zArraySize, double xScale, double zScale, double p_76305_10_)
{
return this.generateNoiseOctaves(result, x, 10, z, xArraySize, 1, zArraySize, xScale, 1.0D, zScale);
}
}
NoiseGeneratorSimplex
用来生成一个Simplex噪声
public class NoiseGeneratorSimplex
{
// 梯度向量
private static int[][] field_151611_e = new int[][] {{1, 1, 0}, { -1, 1, 0}, {1, -1, 0}, { -1, -1, 0}, {1, 0, 1}, { -1, 0, 1}, {1, 0, -1}, { -1, 0, -1}, {0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1}};
// √3
public static final double field_151614_a = Math.sqrt(3.0D);
// 0~255的随机排列
private int[] field_151608_f;
// 座标偏移量
public double field_151612_b;
public double field_151613_c;
public double field_151610_d;
// (√3 - 1) / 2,把二维空间下的单形网格变形成新网格公式中的K1
private static final double field_151609_g = 0.5D * (field_151614_a - 1.0D);
// (3 - √3) / 6,把新网格变回原来空间公式中的K2
private static final double field_151615_h = (3.0D - field_151614_a) / 6.0D;
public NoiseGeneratorSimplex()
{
this(new Random());
}
// 同NoiseGeneratorImproved,略
public NoiseGeneratorSimplex(Random p_i45471_1_)
{
this.field_151608_f = new int[512];
this.field_151612_b = p_i45471_1_.nextDouble() * 256.0D;
this.field_151613_c = p_i45471_1_.nextDouble() * 256.0D;
this.field_151610_d = p_i45471_1_.nextDouble() * 256.0D;
for (int i = 0; i < 256; this.field_151608_f[i] = i++)
{
;
}
for (int l = 0; l < 256; ++l)
{
int j = p_i45471_1_.nextInt(256 - l) + l;
int k = this.field_151608_f[l];
this.field_151608_f[l] = this.field_151608_f[j];
this.field_151608_f[j] = k;
this.field_151608_f[l + 256] = this.field_151608_f[l];
}
}
// 返回不大于num的最大整数
private static int func_151607_a(double num)
{
return num > 0.0D ? (int)num : (int)num - 1;
}
// 返回与二维向量点乘的结果,参数:两个向量
private static double func_151604_a(int[] vector, double xWeight, double zWeight)
{
return (double)vector[0] * xWeight
+ (double)vector[1] * zWeight;
}
// 计算一个点的噪声值
public double func_151605_a(double x, double z)
{
// (√3 - 1) / 2,把二维空间下的单形网格变形成新网格公式中的K1
double K1 = 0.5D * (field_151614_a - 1.0D);
// 把二维空间下的三角形网格变形成新网格
double offset = (x + z) * K1;
// func_151607_a为向下取整,newXZ为该点在新网格的超立方体座标
int newX = func_151607_a(x + offset);
int newZ = func_151607_a(z + offset);
// (3 - √3) / 6,把新网格变回原来空间公式中的K2
double K2 = (3.0D - field_151614_a) / 6.0D;
// 把新网格座标变回三角形网格的座标
double offset2 = (double)(newX + newZ) * K2;
// 点所在三角形左下角的座标
double oldTriangleX = (double)newX - offset2;
double oldTriangleZ = (double)newZ - offset2;
// 点相对于所在三角形的座标
double dx1 = x - oldTriangleX;
double dx2 = z - oldTriangleZ;
// 根据点在哪个三角形内,需要计算第二个顶点索引
int vec2XIndexOffset;
int vec2ZIndexOffset;
if (dx1 > dx2)
{
// 在右下角的三角形内
vec2XIndexOffset = 1;
vec2ZIndexOffset = 0;
}
else
{
// 在左上角的三角形内
vec2XIndexOffset = 0;
vec2ZIndexOffset = 1;
}
int xIndex = newX % 256;
int zIndex = newZ % 256;
// 三个顶点对应的梯度向量索引
int vecIndex1 = this.field_151608_f[xIndex + this.field_151608_f[zIndex]] % 12;
int vecIndex2 = this.field_151608_f[xIndex + vec2XIndexOffset + this.field_151608_f[zIndex + vec2ZIndexOffset]] % 12;
int vecIndex3 = this.field_151608_f[xIndex + 1 + this.field_151608_f[zIndex + 1]] % 12;
// r^2 - dist^2
double tmp1 = 0.5D - dx1 * dx1 - dx2 * dx2;
double res1;
if (tmp1 < 0.0D)
{
// 到该顶点距离太远,贡献为0
res1 = 0.0D;
}
else
{
tmp1 = tmp1 * tmp1;
// 该顶点对结果的贡献度:(r^2 − dist^2)^4 * dot(dist, grad)
res1 = tmp1 * tmp1 * func_151604_a(field_151611_e[vecIndex1], dx1, dx2);
}
double dx2 = dx1 - (double)vec2XIndexOffset + K2;
double dz2 = dx2 - (double)vec2ZIndexOffset + K2;
double tmp2 = 0.5D - dx2 * dx2 - dz2 * dz2;
double res2;
if (tmp2 < 0.0D)
{
res2 = 0.0D;
}
else
{
tmp2 = tmp2 * tmp2;
res2 = tmp2 * tmp2 * func_151604_a(field_151611_e[vecIndex2], dx2, dz2);
}
double dx3 = dx1 - 1.0D + 2.0D * K2;
double dz3 = dx2 - 1.0D + 2.0D * K2;
double tmp3 = 0.5D - dx3 * dx3 - dz3 * dz3;
double res3;
if (tmp3 < 0.0D)
{
res3 = 0.0D;
}
else
{
tmp3 = tmp3 * tmp3;
res3 = tmp3 * tmp3 * func_151604_a(field_151611_e[vecIndex3], dx3, dz3);
}
return 70.0D * (res1 + res2 + res3);
}
// 生成噪声,这个noiseScale和振幅成正比
public void func_151606_a(double[] result, double xOffset, double zOffset, int xSize, int zSize, double xScale, double zScale, double noiseScale)
{
int resultIndex = 0;
for (int _z = 0; _z < zSize; ++_z)
{
double z = (zOffset + (double)_z) * zScale + this.field_151613_c;
for (int _x = 0; _x < xSize; ++_x)
{
double x = (xOffset + (double)_x) * xScale + this.field_151612_b;
// 把二维空间下的三角形网格变形成新网格
double offset = (x + z) * field_151609_g;
// func_151607_a为向下取整,newXZ为该点在新网格的超立方体座标
int newX = func_151607_a(x + offset);
int newZ = func_151607_a(z + offset);
// 把新网格座标变回三角形网格的座标
double offset2 = (double)(newX + newZ) * field_151615_h;
// 点所在三角形左下角的座标
double oldTriangleX = (double)newX - offset2;
double oldTriangleY = (double)newZ - offset2;
// 点相对于所在三角形的座标
double dx1 = x - oldTriangleX;
double dz1 = z - oldTriangleY;
// 根据点在哪个三角形内,需要计算第二个顶点索引
int vec2XIndexOffset;
int vec2ZIndexOffset;
if (dx1 > dz1)
{
// 在右下角的三角形内
vec2XIndexOffset = 1;
vec2ZIndexOffset = 0;
}
else
{
// 在左上角的三角形内
vec2XIndexOffset = 0;
vec2ZIndexOffset = 1;
}
int xIndex = newX % 256;
int zIndex = newZ % 256;
// 三个顶点对应的梯度向量索引
int vecIndex1 = this.field_151608_f[xIndex + this.field_151608_f[zIndex]] % 12;
int vecIndex2 = this.field_151608_f[xIndex + vec2XIndexOffset + this.field_151608_f[zIndex + vec2ZIndexOffset]] % 12;
int vecIndex3 = this.field_151608_f[xIndex + 1 + this.field_151608_f[zIndex + 1]] % 12;
// r^2 - dist^2
double tmp1 = 0.5D - dx1 * dx1 - dz1 * dz1;
double res1;
if (tmp1 < 0.0D)
{
// 到该顶点距离太远,贡献为0
res1 = 0.0D;
}
else
{
tmp1 = tmp1 * tmp1;
// 该顶点对结果的贡献度:(r^2 − dist^2)^4 * dot(dist, grad)
res1 = tmp1 * tmp1 * func_151604_a(field_151611_e[vecIndex1], dx1, dz1);
}
double dx2 = dx1 - (double)vec2XIndexOffset + field_151615_h;
double dz2 = dz1 - (double)vec2ZIndexOffset + field_151615_h;
double tmp2 = 0.5D - dx2 * dx2 - dz2 * dz2;
double res2;
if (tmp2 < 0.0D)
{
res2 = 0.0D;
}
else
{
tmp2 = tmp2 * tmp2;
res2 = tmp2 * tmp2 * func_151604_a(field_151611_e[vecIndex2], dx2, dz2);
}
double dx3 = dx1 - 1.0D + 2.0D * field_151615_h;
double dz3 = dz1 - 1.0D + 2.0D * field_151615_h;
double tmp3 = 0.5D - dx3 * dx3 - dz3 * dz3;
double res3;
if (tmp3 < 0.0D)
{
res3 = 0.0D;
}
else
{
tmp3 = tmp3 * tmp3;
res3 = tmp3 * tmp3 * func_151604_a(field_151611_e[vecIndex3], dx3, dz3);
}
result[resultIndex++] += 70.0D * (res1 + res2 + res3) * noiseScale;
}
}
}
}
NoiseGeneratorPerlin
和NoiseGeneratorOctaves类似,是NoiseGeneratorSimplex的分形叠加
public class NoiseGeneratorPerlin extends NoiseGenerator
{
private NoiseGeneratorSimplex[] field_151603_a;
private int field_151602_b;
// 同NoiseGeneratorOctaves,略
public NoiseGeneratorPerlin(Random p_i45470_1_, int p_i45470_2_)
{
this.field_151602_b = p_i45470_2_;
this.field_151603_a = new NoiseGeneratorSimplex[p_i45470_2_];
for (int i = 0; i < p_i45470_2_; ++i)
{
this.field_151603_a[i] = new NoiseGeneratorSimplex(p_i45470_1_);
}
}
public double func_151601_a(double p_151601_1_, double p_151601_3_)
{
double d0 = 0.0D;
double d1 = 1.0D;
for (int i = 0; i < this.field_151602_b; ++i)
{
d0 += this.field_151603_a[i].func_151605_a(p_151601_1_ * d1, p_151601_3_ * d1) / d1;
d1 /= 2.0D;
}
return d0;
}
// 生成噪声,默认振幅系数0.5
public double[] func_151599_a(double[] p_151599_1_, double p_151599_2_, double p_151599_4_, int p_151599_6_, int p_151599_7_, double p_151599_8_, double p_151599_10_, double p_151599_12_)
{
return this.func_151600_a(p_151599_1_, p_151599_2_, p_151599_4_, p_151599_6_, p_151599_7_, p_151599_8_, p_151599_10_, p_151599_12_, 0.5D);
}
// 生成噪声,_freqScale为每次迭代频率系数(一般取1,不变)
// _noiseScale为每次迭代振幅系数,也会影响频率(一般取0.5,振幅加倍,频率减半)
public double[] func_151600_a(double[] result, double x, double z, int xSize, int zSize, double xScale, double zScale, double _freqScale, double _noiseScale)
{
if (result != null && result.length >= xSize * zSize)
{
// 结果清0,因为后面会叠加
for (int i = 0; i < result.length; ++i)
{
result[i] = 0.0D;
}
}
else
{
result = new double[xSize * zSize];
}
// 和振幅成反比
double noiseScale = 1.0D;
// 和频率成正比
double freqScale = 1.0D;
for (int i = 0; i < this.field_151602_b; ++i)
{
this.field_151603_a[i].func_151606_a(result, x, z, xSize, zSize, xScale * freqScale * noiseScale, zScale * freqScale * noiseScale, 0.55D / noiseScale);
freqScale *= _freqScale;
noiseScale *= _noiseScale;
}
return result;
}
}