using System; namespace Utils { public class PerlinNoise : Utils.Singleton { private int[] _p; // 混淆表 private const int DefaultSeed = 0; // 默认种子 public PerlinNoise() { Initialize(DefaultSeed); } // 初始化混淆表 private void Initialize(int seed) { _p = new int[512]; // 混淆表加倍以方便使用 var permutation = new int[256]; var random = new Random(seed); // 填充数组为0-255 for (var i = 0; i < 256; i++) permutation[i] = i; // 使用Fisher-Yates算法打乱数组 for (var i = 0; i < 256; i++) { var swapIndex = random.Next(256); var temp = permutation[i]; permutation[i] = permutation[swapIndex]; permutation[swapIndex] = temp; } // 将打乱后的数组复制两次,生成512个元素的混淆表 for (var i = 0; i < 256; i++) { _p[i] = permutation[i]; _p[i + 256] = permutation[i]; } } // 重新指定种子 public void Reinitialize(int seed) { Initialize(seed); } // 平滑函数 (6t^5 - 15t^4 + 10t^3) private static double Fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); } // 线性插值 private static double Lerp(double t, double a, double b) { return a + t * (b - a); } // 计算梯度向量和距离向量的点积 private static double Grad(int hash, double x, double y, double z) { return (hash & 0xF) switch // 取hash值的最后4位 { 0x0 => x + y, 0x1 => -x + y, 0x2 => x - y, 0x3 => -x - y, 0x4 => x + z, 0x5 => -x + z, 0x6 => x - z, 0x7 => -x - z, 0x8 => y + z, 0x9 => -y + z, 0xA => y - z, 0xB => -y - z, 0xC => y + x, 0xD => -y + x, 0xE => y - x, 0xF => -y - x, _ => 0 }; } /// /// 为给定的(x, y, z)坐标生成3D Perlin噪声。 /// 输出值通常在-1到1之间。 /// public double Noise(double x, double y=0, double z = 0) { var X = (int)Math.Floor(x) & 255; var Y = (int)Math.Floor(y) & 255; var Z = (int)Math.Floor(z) & 255; x -= Math.Floor(x); y -= Math.Floor(y); z -= Math.Floor(z); var u = Fade(x); var v = Fade(y); var w = Fade(z); var A = _p[X] + Y; var AA = _p[A] + Z; var AB = _p[A + 1] + Z; var B = _p[X + 1] + Y; var BA = _p[B] + Z; var BB = _p[B + 1] + Z; var H000 = _p[AA]; var H100 = _p[BA]; var H010 = _p[AB]; var H110 = _p[BB]; var H001 = _p[AA + 1]; var H101 = _p[BA + 1]; var H011 = _p[AB + 1]; var H111 = _p[BB + 1]; double x0, x1, y0, y1; x0 = Lerp(u, Grad(H000, x, y, z), Grad(H100, x - 1, y, z)); x1 = Lerp(u, Grad(H010, x, y - 1, z), Grad(H110, x - 1, y - 1, z)); y0 = Lerp(v, x0, x1); x0 = Lerp(u, Grad(H001, x, y, z - 1), Grad(H101, x - 1, y, z - 1)); x1 = Lerp(u, Grad(H011, x, y - 1, z - 1), Grad(H111, x - 1, y - 1, z - 1)); y1 = Lerp(v, x0, x1); return Lerp(w, y0, y1); } } }