using UnityEngine; namespace Game.WorldGen { /// /// 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. /// 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; } } /// Classic improved-Perlin gradient noise. Returns roughly [-1, 1]. 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] } /// Fractal Brownian motion (stacked octaves). Returns ~[-1, 1]. 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; } /// Ridged multifractal — sharp mountain crests. Returns [0, 1]. 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; } } }