2025-07-15 15:26:58 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
|
|
|
|
namespace Utils
|
|
|
|
|
{
|
2025-08-05 14:54:30 +08:00
|
|
|
|
public class PerlinNoise : Utils.Singleton<PerlinNoise>
|
2025-07-15 15:26:58 +08:00
|
|
|
|
{
|
2025-08-05 14:54:30 +08:00
|
|
|
|
private int[] _p; // 混淆表
|
|
|
|
|
private const int DefaultSeed = 0; // 默认种子
|
2025-07-15 15:26:58 +08:00
|
|
|
|
|
2025-08-05 14:54:30 +08:00
|
|
|
|
public PerlinNoise()
|
|
|
|
|
{
|
|
|
|
|
Initialize(DefaultSeed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化混淆表
|
|
|
|
|
private void Initialize(int seed)
|
2025-07-15 15:26:58 +08:00
|
|
|
|
{
|
|
|
|
|
_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];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-05 14:54:30 +08:00
|
|
|
|
// 重新指定种子
|
|
|
|
|
public void Reinitialize(int seed)
|
|
|
|
|
{
|
|
|
|
|
Initialize(seed);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 15:26:58 +08:00
|
|
|
|
// 平滑函数 (6t^5 - 15t^4 + 10t^3)
|
2025-08-05 14:54:30 +08:00
|
|
|
|
private static double Fade(double t)
|
2025-07-15 15:26:58 +08:00
|
|
|
|
{
|
|
|
|
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 线性插值
|
2025-08-05 14:54:30 +08:00
|
|
|
|
private static double Lerp(double t, double a, double b)
|
2025-07-15 15:26:58 +08:00
|
|
|
|
{
|
|
|
|
|
return a + t * (b - a);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算梯度向量和距离向量的点积
|
2025-08-05 14:54:30 +08:00
|
|
|
|
private static double Grad(int hash, double x, double y, double z)
|
2025-07-15 15:26:58 +08:00
|
|
|
|
{
|
2025-08-05 14:54:30 +08:00
|
|
|
|
return (hash & 0xF) switch // 取hash值的最后4位
|
2025-07-15 15:26:58 +08:00
|
|
|
|
{
|
2025-08-05 14:54:30 +08:00
|
|
|
|
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
|
|
|
|
|
};
|
2025-07-15 15:26:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 为给定的(x, y, z)坐标生成3D Perlin噪声。
|
|
|
|
|
/// 输出值通常在-1到1之间。
|
|
|
|
|
/// </summary>
|
2025-08-05 14:54:30 +08:00
|
|
|
|
public double Noise(double x, double y=0, double z = 0)
|
2025-07-15 15:26:58 +08:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2025-08-05 14:54:30 +08:00
|
|
|
|
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));
|
2025-07-15 15:26:58 +08:00
|
|
|
|
y0 = Lerp(v, x0, x1);
|
|
|
|
|
|
2025-08-05 14:54:30 +08:00
|
|
|
|
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));
|
2025-07-15 15:26:58 +08:00
|
|
|
|
y1 = Lerp(v, x0, x1);
|
|
|
|
|
|
|
|
|
|
return Lerp(w, y0, y1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|