98 lines
3.4 KiB
C#
98 lines
3.4 KiB
C#
using UnityEngine;
|
|
|
|
namespace Game.WorldGen
|
|
{
|
|
/// <summary>
|
|
/// Deterministic, seedable 2D gradient (Perlin) noise plus fBm / ridged variants.
|
|
/// All sampling is done in continuous world space so neighbouring terrain tiles
|
|
/// share identical edge values automatically -> seamless stitching by construction.
|
|
/// </summary>
|
|
public sealed class NoiseSampler
|
|
{
|
|
readonly int[] _perm = new int[512];
|
|
|
|
public NoiseSampler(int seed)
|
|
{
|
|
var p = new int[256];
|
|
for (int i = 0; i < 256; i++) p[i] = i;
|
|
|
|
// Fisher-Yates shuffle driven by the seed -> reproducible permutation table.
|
|
var rng = new System.Random(seed);
|
|
for (int i = 255; i > 0; i--)
|
|
{
|
|
int j = rng.Next(i + 1);
|
|
(p[i], p[j]) = (p[j], p[i]);
|
|
}
|
|
for (int i = 0; i < 512; i++) _perm[i] = p[i & 255];
|
|
}
|
|
|
|
static float Fade(float t) => t * t * t * (t * (t * 6f - 15f) + 10f);
|
|
|
|
static float Grad(int hash, float x, float y)
|
|
{
|
|
switch (hash & 7)
|
|
{
|
|
case 0: return x + y;
|
|
case 1: return -x + y;
|
|
case 2: return x - y;
|
|
case 3: return -x - y;
|
|
case 4: return x;
|
|
case 5: return -x;
|
|
case 6: return y;
|
|
default: return -y;
|
|
}
|
|
}
|
|
|
|
/// <summary>Classic improved-Perlin gradient noise. Returns roughly [-1, 1].</summary>
|
|
public float Perlin(float x, float y)
|
|
{
|
|
int xi = Mathf.FloorToInt(x) & 255;
|
|
int yi = Mathf.FloorToInt(y) & 255;
|
|
float xf = x - Mathf.Floor(x);
|
|
float yf = y - Mathf.Floor(y);
|
|
|
|
float u = Fade(xf);
|
|
float v = Fade(yf);
|
|
|
|
int aa = _perm[_perm[xi] + yi];
|
|
int ab = _perm[_perm[xi] + yi + 1];
|
|
int ba = _perm[_perm[xi + 1] + yi];
|
|
int bb = _perm[_perm[xi + 1] + yi + 1];
|
|
|
|
float x1 = Mathf.Lerp(Grad(aa, xf, yf), Grad(ba, xf - 1f, yf), u);
|
|
float x2 = Mathf.Lerp(Grad(ab, xf, yf - 1f), Grad(bb, xf - 1f, yf - 1f), u);
|
|
return Mathf.Lerp(x1, x2, v); // ~[-1, 1]
|
|
}
|
|
|
|
/// <summary>Fractal Brownian motion (stacked octaves). Returns ~[-1, 1].</summary>
|
|
public float Fbm(float x, float y, int octaves, float lacunarity, float gain)
|
|
{
|
|
float sum = 0f, amp = 1f, freq = 1f, norm = 0f;
|
|
for (int i = 0; i < octaves; i++)
|
|
{
|
|
sum += amp * Perlin(x * freq, y * freq);
|
|
norm += amp;
|
|
amp *= gain;
|
|
freq *= lacunarity;
|
|
}
|
|
return norm > 0f ? sum / norm : 0f;
|
|
}
|
|
|
|
/// <summary>Ridged multifractal — sharp mountain crests. Returns [0, 1].</summary>
|
|
public float Ridged(float x, float y, int octaves, float lacunarity, float gain)
|
|
{
|
|
float sum = 0f, amp = 1f, freq = 1f, norm = 0f;
|
|
for (int i = 0; i < octaves; i++)
|
|
{
|
|
float n = 1f - Mathf.Abs(Perlin(x * freq, y * freq));
|
|
n *= n; // sharpen the ridges
|
|
sum += amp * n;
|
|
norm += amp;
|
|
amp *= gain;
|
|
freq *= lacunarity;
|
|
}
|
|
return norm > 0f ? sum / norm : 0f;
|
|
}
|
|
}
|
|
}
|