1+ #pragma once
2+
3+
4+ #include " Utility/PerlinNoise.hpp"
5+ #include " Utility/Performance.hpp"
6+
7+ #include < cmath>
8+
9+ namespace Utility
10+ {
11+ struct Perlin
12+ {
13+ struct Params
14+ {
15+ uint8_t octaves = 7u ; // number of noise layers
16+ float scale = 375 .f; // world-to-noise scale
17+ float persistence = 1 .3f ; // amplitude falloff per octave
18+ float lacunarity = 2 .3f ; // frequency multiplier per octave
19+ float exponentiation = 8 .5f ; // curve‑shaping exponent
20+ float height = 2048 .f; // final amplitude multiplier
21+ };
22+
23+ static float Get (float x, float y, const Params& params = Params{})
24+ {
25+ PERF (GeneratePerlinNoise);
26+
27+ float xs = x / params.scale ;
28+ float ys = y / params.scale ;
29+ float G = std::pow (2 .f , -params.persistence );
30+ float amp = 1 .f ;
31+ float freq = 1 .f ;
32+ float norm = 0 .f ;
33+ float total = 0 .f ;
34+ auto perlin = siv::BasicPerlinNoise<float >{};
35+
36+ for (uint8_t o = 0 ; o < params.octaves ; ++o)
37+ {
38+ float nv = perlin.noise2D (xs * freq, ys * freq) * 0 .5f + 0 .5f ;
39+ total += nv * amp;
40+ norm += amp;
41+ amp *= G;
42+ freq *= params.lacunarity ;
43+ }
44+
45+ total /= norm;
46+ return std::pow (total, params.exponentiation ) * params.height ;
47+ }
48+
49+
50+ struct NoiseResult
51+ {
52+ float value; // Noise value
53+ float dx; // Partial derivative w.r.t x
54+ float dy; // Partial derivative w.r.t y
55+ };
56+ static NoiseResult noise2DWithDerivative (const siv::BasicPerlinNoise<float >& noise, float x, float y) noexcept
57+ {
58+ auto FadeDerivative = [](float t) noexcept { return 30 * t * t * (t * (t - 2 ) + 1 ); };
59+
60+ constexpr float Z = static_cast <float >(SIVPERLIN_DEFAULT_Z);
61+ const float X0 = std::floor (x);
62+ const float Y0 = std::floor (y);
63+ const float Z0 = std::floor (Z);
64+
65+ const int32_t ix = static_cast <int32_t >(X0) & 255 ;
66+ const int32_t iy = static_cast <int32_t >(Y0) & 255 ;
67+ const int32_t iz = static_cast <int32_t >(Z0) & 255 ;
68+
69+ const float fx = x - X0;
70+ const float fy = y - Y0;
71+ const float fz = Z - Z0;
72+
73+ const float u = siv::perlin_detail::Fade (fx);
74+ const float v = siv::perlin_detail::Fade (fy);
75+ const float w = siv::perlin_detail::Fade (fz);
76+ const float du = FadeDerivative (fx);
77+ const float dv = FadeDerivative (fy);
78+ // w derivative is zero because z is constant
79+
80+ auto & permutation = noise.serialize ();
81+
82+ const uint8_t A = (permutation[ix] + iy) & 255 ;
83+ const uint8_t B = (permutation[(ix + 1 ) & 255 ] + iy) & 255 ;
84+
85+ const uint8_t AA = (permutation[A] + iz) & 255 ;
86+ const uint8_t AB = (permutation[(A + 1 ) & 255 ] + iz) & 255 ;
87+ const uint8_t BA = (permutation[B] + iz) & 255 ;
88+ const uint8_t BB = (permutation[(B + 1 ) & 255 ] + iz) & 255 ;
89+
90+ // Gradients at each corner
91+ const float g000 = siv::perlin_detail::Grad (permutation[AA], fx, fy, fz);
92+ const float g100 = siv::perlin_detail::Grad (permutation[BA], fx - 1 , fy, fz);
93+ const float g010 = siv::perlin_detail::Grad (permutation[AB], fx, fy - 1 , fz);
94+ const float g110 = siv::perlin_detail::Grad (permutation[BB], fx - 1 , fy - 1 , fz);
95+ const float g001 = siv::perlin_detail::Grad (permutation[(AA + 1 ) & 255 ], fx, fy, fz - 1 );
96+ const float g101 = siv::perlin_detail::Grad (permutation[(BA + 1 ) & 255 ], fx - 1 , fy, fz - 1 );
97+ const float g011 = siv::perlin_detail::Grad (permutation[(AB + 1 ) & 255 ], fx, fy - 1 , fz - 1 );
98+ const float g111 = siv::perlin_detail::Grad (permutation[(BB + 1 ) & 255 ], fx - 1 , fy - 1 , fz - 1 );
99+
100+ const float x00 = siv::perlin_detail::Lerp (g000, g100, u);
101+ const float x10 = siv::perlin_detail::Lerp (g010, g110, u);
102+ const float x01 = siv::perlin_detail::Lerp (g001, g101, u);
103+ const float x11 = siv::perlin_detail::Lerp (g011, g111, u);
104+
105+ const float y0 = siv::perlin_detail::Lerp (x00, x10, v);
106+ const float y1 = siv::perlin_detail::Lerp (x01, x11, v);
107+
108+ const float value = siv::perlin_detail::Lerp (y0, y1, w);
109+
110+ // Partial derivative w.r.t x
111+ const float dx00 = (g100 - g000);
112+ const float dx10 = (g110 - g010);
113+ const float dx01 = (g101 - g001);
114+ const float dx11 = (g111 - g011);
115+
116+ const float dy0 = siv::perlin_detail::Lerp (dx00, dx10, v);
117+ const float dy1 = siv::perlin_detail::Lerp (dx01, dx11, v);
118+ const float dx = du * siv::perlin_detail::Lerp (dy0, dy1, w);
119+
120+ // Partial derivative w.r.t y
121+ const float dy00 = (x10 - x00);
122+ const float dy01 = (x11 - x01);
123+ const float dy = dv * siv::perlin_detail::Lerp (dy00, dy01, w);
124+
125+ return { value, dx, dy };
126+ }
127+
128+ struct Result
129+ {
130+ float height;
131+ glm::vec3 normal;
132+ };
133+ static Result GetWithNormal (float x, float y, const Params& params = Params{})
134+ {
135+ PERF (GeneratePerlinNoiseWithNormal);
136+
137+ float xs = x / params.scale ;
138+ float ys = y / params.scale ;
139+ float G = std::pow (2 .0f , -params.persistence );
140+ float amp = 1 .f ;
141+ float freq = 1 .f ;
142+ float norm = 0 .f ;
143+ float total = 0 .f ;
144+ float dx_total = 0 .f ;
145+ float dy_total = 0 .f ;
146+ auto perlin = siv::BasicPerlinNoise<float >{};
147+
148+ for (uint8_t o = 0 ; o < params.octaves ; ++o)
149+ {
150+ auto result = noise2DWithDerivative (perlin, xs * freq, ys * freq);
151+
152+ float nv = result.value * 0 .5f + 0 .5f ;
153+ total += nv * amp;
154+
155+ // derivative scaled by amp
156+ dx_total += result.dx * freq * amp;
157+ dy_total += result.dy * freq * amp;
158+
159+ norm += amp;
160+ amp *= G;
161+ freq *= params.lacunarity ;
162+ }
163+
164+ total /= norm;
165+ dx_total /= norm;
166+ dy_total /= norm;
167+
168+ // Apply exponentiation curve to height
169+ float finalHeight = std::pow (total, params.exponentiation ) * params.height ;
170+
171+ // Apply exponentiation chain rule for derivatives
172+ float exponentFactor = params.exponentiation * std::pow (total, params.exponentiation - 1 ) * params.height ;
173+
174+
175+
176+ glm::vec3 normal = glm::normalize (glm::vec3 (
177+ -dx_total * exponentFactor / params.scale ,
178+ 1 .0f ,
179+ -dy_total * exponentFactor / params.scale ));
180+
181+ // normal = glm::normalize(glm::vec3(dx_total, 1.0f, dy_total));
182+
183+ return {finalHeight, normal};
184+ }
185+ };
186+ } // namespace Utility
0 commit comments