18auto wang_hash(uint32_t seed) -> uint32_t
20 seed = (seed ^ 61u) ^ (seed >> 16u);
22 seed = seed ^ (seed >> 4u);
24 seed = seed ^ (seed >> 15u);
28auto hash_int3(
int x,
int y,
int z) ->
float
30 uint32_t
h = wang_hash(
31 static_cast<uint32_t
>(
x) * 73856093u ^
32 static_cast<uint32_t
>(
y) * 19349663u ^
33 static_cast<uint32_t
>(
z) * 83492791u);
34 return static_cast<float>(
h) /
static_cast<float>(0xFFFFFFFFu);
42auto hash_int3_vec3(
int x,
int y,
int z) ->
f3
44 float hx = hash_int3(
x,
y,
z);
45 float hy = hash_int3(
x + 47,
y + 17,
z + 31);
46 float hz = hash_int3(
x + 89,
y + 53,
z + 71);
54auto wrap(
int v) ->
int
56 return ((
v % period) + period) %
period;
61auto lerp(
float a,
float b,
float t) ->
float {
return a + (
b -
a) * t; }
63auto smoothstep_f(
float t) ->
float
65 t = std::clamp(t, 0.0f, 1.0f);
66 return t *
t * (3.0f - 2.0f *
t);
71auto value_noise_3d(
float px,
float py,
float pz) ->
float
73 int ix =
static_cast<int>(std::floor(px));
74 int iy =
static_cast<int>(std::floor(py));
75 int iz =
static_cast<int>(std::floor(pz));
77 float fx = px - std::floor(px);
78 float fy = py - std::floor(py);
79 float fz = pz - std::floor(pz);
81 float ux = fx * fx * (3.0f - 2.0f * fx);
82 float uy = fy * fy * (3.0f - 2.0f * fy);
83 float uz = fz * fz * (3.0f - 2.0f * fz);
85 int i0x = wrap(ix), i0y = wrap(iy), i0z = wrap(iz);
86 int i1x = wrap(ix + 1), i1y = wrap(iy + 1), i1z = wrap(iz + 1);
88 float n000 = hash_int3(i0x, i0y, i0z);
89 float n100 = hash_int3(i1x, i0y, i0z);
90 float n010 = hash_int3(i0x, i1y, i0z);
91 float n110 = hash_int3(i1x, i1y, i0z);
92 float n001 = hash_int3(i0x, i0y, i1z);
93 float n101 = hash_int3(i1x, i0y, i1z);
94 float n011 = hash_int3(i0x, i1y, i1z);
95 float n111 = hash_int3(i1x, i1y, i1z);
97 float nx00 =
lerp(n000, n100, ux);
98 float nx10 =
lerp(n010, n110, ux);
99 float nx01 =
lerp(n001, n101, ux);
100 float nx11 =
lerp(n011, n111, ux);
102 float nxy0 =
lerp(nx00, nx10, uy);
103 float nxy1 =
lerp(nx01, nx11, uy);
105 return lerp(nxy0, nxy1, uz);
110auto worley_noise_3d(
float px,
float py,
float pz) ->
float
112 int ix =
static_cast<int>(std::floor(px));
113 int iy =
static_cast<int>(std::floor(py));
114 int iz =
static_cast<int>(std::floor(pz));
116 float fx = px - std::floor(px);
117 float fy = py - std::floor(py);
118 float fz = pz - std::floor(pz);
120 float min_dist = 1.0f;
122 for(
int dx = -1; dx <= 1; dx++)
124 for(
int dy = -1; dy <= 1; dy++)
126 for(
int dz = -1; dz <= 1; dz++)
128 int cx = wrap(ix + dx);
129 int cy = wrap(iy + dy);
130 int cz = wrap(iz + dz);
132 f3 pt = hash_int3_vec3(cx, cy, cz);
134 float diff_x = float(dx) + pt.x - fx;
135 float diff_y = float(dy) + pt.y - fy;
136 float diff_z = float(dz) + pt.z - fz;
138 float dist = diff_x * diff_x + diff_y * diff_y + diff_z * diff_z;
139 min_dist = std::min(min_dist,
dist);
143 return std::sqrt(min_dist);
148auto fbm_value(
float px,
float py,
float pz,
int octaves) ->
float
151 float amplitude = 0.5f;
153 for(
int i = 0;
i < octaves;
i++)
155 value += amplitude * value_noise_3d(px * freq, py * freq, pz * freq);
162auto worley_at(
float px,
float py,
float pz,
float freq) ->
float
164 return worley_noise_3d(px * freq, py * freq, pz * freq);
169auto remap(
float value,
float lo,
float hi,
float new_lo,
float new_hi) ->
float
171 return new_lo + (value - lo) / (hi - lo) * (new_hi - new_lo);
176auto float_to_half(
float value) -> uint16_t
179 std::memcpy(&f, &value, 4);
181 uint32_t sign = (
f >> 16u) & 0x8000u;
182 auto exponent =
static_cast<int32_t
>(((
f >> 23u) & 0xFFu)) - 127 + 15;
183 uint32_t mantissa =
f & 0x007FFFFFu;
187 return static_cast<uint16_t
>(sign);
191 return static_cast<uint16_t
>(sign | 0x7C00u);
194 return static_cast<uint16_t
>(sign | (
static_cast<uint32_t
>(exponent) << 10u) | (mantissa >> 13u));
211void cloud_noise_textures::generate_3d()
214 constexpr size_t total = size_t(res) * res * res;
215 constexpr size_t num_channels = 4;
216 constexpr size_t num_halfs = total * num_channels;
217 constexpr size_t bytes = num_halfs *
sizeof(uint16_t);
219 APPLOG_TRACE(
"[CloudNoise] Generating {}x{}x{} 3D noise texture (RGBA16F, period={})...",
222 std::vector<uint16_t> data(num_halfs);
224 const float inv_res = float(
tile_period) / float(res);
226 for(uint16_t
z = 0;
z < res;
z++)
228 for(uint16_t
y = 0;
y < res;
y++)
230 for(uint16_t
x = 0;
x < res;
x++)
232 float px = float(
x) * inv_res;
233 float py = float(
y) * inv_res;
234 float pz = float(
z) * inv_res;
237 float perlin4 = fbm_value(px, py, pz, 4);
240 float inv_worley = 1.0f - worley_at(px, py, pz, 1.0f);
243 float perlin_worley = perlin4 * 0.55f + inv_worley * 0.45f;
244 perlin_worley = std::clamp(perlin_worley, 0.0f, 1.0f);
247 float worley_1x = worley_at(px, py, pz, 1.0f);
248 float worley_2x = worley_at(px, py, pz, 2.0f);
249 float worley_4x = worley_at(px, py, pz, 4.0f);
251 size_t idx = (size_t(
z) * res * res + size_t(
y) * res + size_t(
x)) * num_channels;
252 data[idx + 0] = float_to_half(perlin_worley);
253 data[idx + 1] = float_to_half(worley_1x);
254 data[idx + 2] = float_to_half(worley_2x);
255 data[idx + 3] = float_to_half(worley_4x);
260 APPLOG_TRACE(
"[CloudNoise] Noise computed, uploading to GPU...");
266 gfx::texture_format::RGBA16F,
267 BGFX_TEXTURE_NONE | BGFX_SAMPLER_NONE,
270 APPLOG_TRACE(
"[CloudNoise] 3D noise texture ready ({} MB).",
bytes /
size_t(1024 * 1024));
273void cloud_noise_textures::generate_flat()
276 constexpr size_t total = size_t(res) * res;
277 constexpr size_t num_channels = 4;
278 constexpr size_t num_halfs = total * num_channels;
279 constexpr size_t bytes = num_halfs *
sizeof(uint16_t);
281 APPLOG_TRACE(
"[CloudNoise] Generating {}x{} 2D flat noise texture (RGBA16F, period={})...",
284 std::vector<uint16_t> data(num_halfs);
286 const float inv_res = float(
tile_period) / float(res);
289 for(uint16_t
y = 0;
y < res;
y++)
291 for(uint16_t
x = 0;
x < res;
x++)
293 float px = float(
x) * inv_res;
294 float py = float(
y) * inv_res;
296 float perlin4 = fbm_value(px, fixed_z, py, 4);
297 float inv_worley = 1.0f - worley_at(px, fixed_z, py, 1.0f);
299 float perlin_worley = perlin4 * 0.65f + inv_worley * 0.35f;
300 perlin_worley = std::clamp(perlin_worley, 0.0f, 1.0f);
302 float worley_1x = worley_at(px, fixed_z, py, 1.0f);
303 float worley_2x = worley_at(px, fixed_z, py, 2.0f);
304 float worley_4x = worley_at(px, fixed_z, py, 4.0f);
306 size_t idx = (size_t(
y) * res + size_t(
x)) * num_channels;
307 data[idx + 0] = float_to_half(perlin_worley);
308 data[idx + 1] = float_to_half(worley_1x);
309 data[idx + 2] = float_to_half(worley_2x);
310 data[idx + 3] = float_to_half(worley_4x);
314 APPLOG_TRACE(
"[CloudNoise] 2D flat noise computed, uploading to GPU...");
321 gfx::texture_format::RGBA16F,
322 BGFX_TEXTURE_NONE | BGFX_SAMPLER_NONE,
325 APPLOG_TRACE(
"[CloudNoise] 2D flat noise texture ready ({} KB).",
bytes /
size_t(1024));
#define APPLOG_TRACE(...)
const memory_view * copy(const void *_data, uint32_t _size)
auto lerp(const T &start, const T &end, float progress, const ease_t &ease_func=ease::linear) -> T
Linearly interpolates between two values based on progress.
static constexpr uint16_t resolution
static constexpr uint16_t flat_resolution
std::unique_ptr< gfx::texture > flat_noise
256x256 RGBA16F 2D tileable noise for flat clouds.
static constexpr int tile_period
std::unique_ptr< gfx::texture > base_noise
64^3 RGBA16F 3D tileable noise for volumetric clouds.