Files
godot-------/Script/Base/PerlinNoise.cs
m0_75251201 7700703099 初次提交
2025-07-12 11:30:22 +08:00

135 lines
4.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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; // 不应该发生
}
}
/// <summary>
/// 为给定的(x, y, z)坐标生成3D Perlin噪声。
/// 输出值通常在-1到1之间。
/// </summary>
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);
}
}
}