接下來的地形生成代碼會接觸到噪聲函數,所以本篇內容就先逆向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;
}
}