Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/Utils/PerlinNoise.cs

156 lines
4.8 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 Utils
{
public class PerlinNoise : Singleton<PerlinNoise>
{
private readonly int[] _p = new int[512]; // 混淆表
public PerlinNoise()
{
Initialize();
}
/// <summary>
/// 初始化混淆表。
/// </summary>
/// <param name="seed">随机种子。</param>
private void Initialize(int seed = 0)
{
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);
(permutation[i], permutation[swapIndex]) = (permutation[swapIndex], permutation[i]);
}
// 将打乱后的数组复制两次生成512个元素的混淆表
for (var i = 0; i < 256; i++)
{
_p[i] = permutation[i];
_p[i + 256] = permutation[i];
}
}
/// <summary>
/// 重新指定种子并初始化混淆表。
/// </summary>
/// <param name="seed">随机种子。</param>
public void Reinitialize(int seed)
{
Initialize(seed);
}
/// <summary>
/// Perlin噪声平滑函数 (6t^5 - 15t^4 + 10t^3)。
/// </summary>
/// <param name="t">输入值。</param>
/// <returns>平滑后的值。</returns>
private static double Fade(double t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
/// <summary>
/// 线性插值。
/// </summary>
/// <param name="t">插值因子 (0-1)。</param>
/// <param name="a">起始值。</param>
/// <param name="b">结束值。</param>
/// <returns>插值结果。</returns>
private static double Lerp(double t, double a, double b)
{
return a + t * (b - a);
}
/// <summary>
/// 计算梯度向量和距离向量的点积。
/// </summary>
/// <param name="hash">哈希值,用于决定梯度向量方向。</param>
/// <param name="x">X轴距离。</param>
/// <param name="y">Y轴距离。</param>
/// <param name="z">Z轴距离。</param>
/// <returns>点积结果。</returns>
private static double Grad(int hash, double x, double y, double z)
{
return (hash & 0xF) switch // 取哈希值的最后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
};
}
/// <summary>
/// 为给定的(x, y, z)坐标生成3D Perlin噪声。
/// 输出值通常在-1到1之间。
/// </summary>
/// <param name="x">X坐标。</param>
/// <param name="y">Y坐标默认为0。</param>
/// <param name="z">Z坐标默认为0。</param>
/// <returns>Perlin噪声值。</returns>
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);
}
}
}