using System; namespace Cosmobox { public class PerlinNoise { private int[] p; // 混淆表 // 构造函数:初始化混淆表 public PerlinNoise(int seed) { // 初始化为0-255的随机排列 p = new int[512]; // 混淆表加倍以方便使用 int[] permutation = new int[256]; Random random = new Random(seed); // 填充数组为0-255 for (int i = 0; i < 256; i++) { permutation[i] = i; } // 使用Fisher-Yates算法打乱数组 for (int i = 0; i < 256; i++) { int swapIndex = random.Next(256); int temp = permutation[i]; permutation[i] = permutation[swapIndex]; permutation[swapIndex] = temp; } // 将打乱后的数组复制两次,生成512个元素的混淆表 for (int i = 0; i < 256; i++) { p[i] = permutation[i]; p[i + 256] = permutation[i]; } } // 平滑函数 (6t^5 - 15t^4 + 10t^3) private double Fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); } // 线性插值 private double Lerp(double t, double a, double b) { return a + t * (b - a); } // 计算梯度向量和距离向量的点积 private double Grad(int hash, double x, double y, double z) { // 根据hash值确定使用哪个梯度向量 // 12个梯度向量由以下组合构成:(+/-1, +/-1, 0), (+/-1, 0, +/-1), (0, +/-1, +/-1) switch (hash & 0xF) // 取hash值的最后4位 { case 0x0: return x + y; case 0x1: return -x + y; case 0x2: return x - y; case 0x3: return -x - y; case 0x4: return x + z; case 0x5: return -x + z; case 0x6: return x - z; case 0x7: return -x - z; case 0x8: return y + z; case 0x9: return -y + z; case 0xA: return y - z; case 0xB: return -y - z; case 0xC: return y + x; // 这四个是重复的,但Ken Perlin的原始实现中包含它们。 case 0xD: return -y + x; // 它们对噪声质量影响不大,但保持了表格的一致性。 case 0xE: return y - x; case 0xF: return -y - x; default: return 0; // 不应该发生 } } /// /// 为给定的(x, y, z)坐标生成3D Perlin噪声。 /// 输出值通常在-1到1之间。 /// public double Noise(double x, double y, double z) { // 找到包含该点的单位立方体 int X = (int)Math.Floor(x) & 255; int Y = (int)Math.Floor(y) & 255; int Z = (int)Math.Floor(z) & 255; // 找到该点在立方体内的相对x, y, z坐标 x -= Math.Floor(x); y -= Math.Floor(y); z -= Math.Floor(z); // 计算x, y, z的平滑曲线 double u = Fade(x); double v = Fade(y); double w = Fade(z); // 对立方体的8个角进行哈希计算 int A = p[X] + Y; int AA = p[A] + Z; int AB = p[A + 1] + Z; int B = p[X + 1] + Y; int BA = p[B] + Z; int BB = p[B + 1] + Z; // 获取所有8个角的哈希值 int H000 = p[AA]; int H100 = p[BA]; int H010 = p[AB]; int H110 = p[BB]; int H001 = p[AA + 1]; int H101 = p[BA + 1]; int H011 = p[AB + 1]; int H111 = p[BB + 1]; // 计算所有8个角的点积并插值 double x0, x1, y0, y1; x0 = Lerp(u, Grad(H000, x, y, z), // (0,0,0) Grad(H100, x - 1, y, z)); // (1,0,0) x1 = Lerp(u, Grad(H010, x, y - 1, z), // (0,1,0) Grad(H110, x - 1, y - 1, z)); // (1,1,0) y0 = Lerp(v, x0, x1); x0 = Lerp(u, Grad(H001, x, y, z - 1), // (0,0,1) Grad(H101, x - 1, y, z - 1)); // (1,0,1) x1 = Lerp(u, Grad(H011, x, y - 1, z - 1), // (0,1,1) Grad(H111, x - 1, y - 1, z - 1)); // (1,1,1) y1 = Lerp(v, x0, x1); return Lerp(w, y0, y1); } } }