bevy_elements 0.7.4

Living environments for Bevy: sun, moon and stars, climate-driven weather, GPU wind, volumetric clouds, rain, snow and fog, lightning, terrain with hydraulic and thermal erosion, flooding, snow cover, wetness and dust — simulated in compute shaders.
Documentation
// Cloud noise textures, generated once by compute (the recipe of Hillaire's TileableVolumeNoise,
// which realises Schneider 2015). Every channel tiles with the texture period.
//
//   base   128³: R Perlin-Worley, G/B/A Worley fBm at 2/4/8-cell octaves (cells ×4)
//   detail  32³: R/G/B Worley fBm at 1/2/4/8-cell octaves (cells ×2)
//
// See docs/research/clouds.md ("Noise textures"). One kernel per texture; the base texture is
// produced in four z-slabs over four frames (`params.tail.x` is the first slice of the slab).

import bevy_elements::noise::{worley3, fbm3_periodic};
import bevy_elements::clouds::common::CloudParams;

@group(0) @binding(0) var<uniform> params: CloudParams;
@group(0) @binding(1) var base_out: texture_storage_3d<rgba8unorm, write>;
@group(0) @binding(2) var detail_out: texture_storage_3d<rgba8unorm, write>;

/// Largest |fbm| of the 3-octave periodic Perlin sum, measured by
/// `the_noise_normalisation_constants_match_the_noise` over 10⁵ points.
const PERLIN_FBM_MAX: f32 = 0.55;

/// Inverted Worley noise with `k` cells across the tile; `x` is in [0, 1).
fn inverted_worley(x: vec3<f32>, k: i32) -> f32 {
    return 1.0 - min(worley3(x * f32(k), k), 1.0);
}

@compute @workgroup_size(4, 4, 4)
fn cloud_noise_base(@builtin(global_invocation_id) id: vec3<u32>) {
    let z = id.z + params.tail.x;
    if id.x >= 128u || id.y >= 128u || z >= 128u {
        return;
    }
    let texel = vec3<u32>(id.x, id.y, z);
    let uvw = (vec3<f32>(texel) + 0.5) / 128.0;
    let w8 = inverted_worley(uvw, 8);
    let w16 = inverted_worley(uvw, 16);
    let w32 = inverted_worley(uvw, 32);
    let w56 = inverted_worley(uvw, 56);
    let w64 = inverted_worley(uvw, 64);
    let perlin = clamp(0.5 + 0.5 * fbm3_periodic(uvw * 8.0, 8, 3) / PERLIN_FBM_MAX, 0.0, 1.0);
    let dilated = 0.625 * w8 + 0.25 * w32 + 0.125 * w56;
    let r = dilated + perlin * (1.0 - dilated);
    let g = 0.625 * w8 + 0.25 * w16 + 0.125 * w32;
    let b = 0.625 * w16 + 0.25 * w32 + 0.125 * w64;
    let a = 0.75 * w32 + 0.25 * w64;
    textureStore(base_out, vec3<i32>(texel), vec4<f32>(r, g, b, a));
}

@compute @workgroup_size(4, 4, 4)
fn cloud_noise_detail(@builtin(global_invocation_id) id: vec3<u32>) {
    if any(id >= vec3<u32>(32u)) {
        return;
    }
    let uvw = (vec3<f32>(id) + 0.5) / 32.0;
    let w2 = inverted_worley(uvw, 2);
    let w4 = inverted_worley(uvw, 4);
    let w8 = inverted_worley(uvw, 8);
    let w16 = inverted_worley(uvw, 16);
    let r = 0.625 * w2 + 0.25 * w4 + 0.125 * w8;
    let g = 0.625 * w4 + 0.25 * w8 + 0.125 * w16;
    let b = 0.75 * w8 + 0.25 * w16;
    textureStore(detail_out, vec3<i32>(id), vec4<f32>(r, g, b, 1.0));
}