#[compute]
#version 450
// Fills a 3D texture with Nubis cloud density.
// Same Schneider density function as the sky shader but evaluated on a grid.
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
layout(set = 0, binding = 0, r16f) uniform restrict writeonly image3D density_out;
layout(set = 0, binding = 1) uniform sampler3D perlworlnoise;
layout(set = 0, binding = 2) uniform sampler3D worlnoise;
layout(set = 0, binding = 3) uniform sampler2D weather_map;
layout(push_constant, std430) uniform Params {
vec4 volume_origin; // xyz = world position of volume center, w = grid_size
vec4 volume_size; // xyz = volume dimensions in meters, w = coverage
vec4 wind_time; // x = time, y = time_scale, z = wind_dir_x, w = wind_dir_z
} p;
float remap(float v, float lo, float hi, float nlo, float nhi) {
return nlo + (v - lo) / (hi - lo) * (nhi - nlo);
}
float density_height_gradient(float height_frac, float cloud_type) {
const vec4 STRATUS = vec4(0.02, 0.05, 0.09, 0.11);
const vec4 STRATOCUMULUS = vec4(0.02, 0.2, 0.48, 0.625);
const vec4 CUMULUS = vec4(0.01, 0.0625, 0.78, 1.0);
float st = 1.0 - clamp(cloud_type * 2.0, 0.0, 1.0);
float sc = 1.0 - abs(cloud_type - 0.5) * 2.0;
float cu = clamp(cloud_type - 0.5, 0.0, 1.0) * 2.0;
vec4 g = STRATUS * st + STRATOCUMULUS * sc + CUMULUS * cu;
return smoothstep(g.x, g.y, height_frac) - smoothstep(g.z, g.w, height_frac);
}
void main() {
ivec3 gid = ivec3(gl_GlobalInvocationID);
int grid_size = int(p.volume_origin.w);
if (gid.x >= grid_size || gid.y >= grid_size || gid.z >= grid_size) return;
// Map grid position to world space
vec3 uvw = (vec3(gid) + 0.5) / float(grid_size);
vec3 world_pos = p.volume_origin.xyz + (uvw - 0.5) * p.volume_size.xyz;
float height_frac = uvw.y;
float coverage = p.volume_size.w;
// Wind offset
float wind_time = p.wind_time.x * p.wind_time.y;
vec3 sample_pos = world_pos;
sample_pos.x += 10.0 * wind_time;
// Sample base noise (same scale as sky shader)
vec4 n = textureLod(perlworlnoise, sample_pos * 0.00008, 0.0);
float fbm = n.g * 0.625 + n.b * 0.25 + n.a * 0.125;
// Height gradient
float g = density_height_gradient(height_frac, 0.5); // stratocumulus-cumulus blend
// Base cloud shape
float base_cloud = remap(n.r, -(1.0 - fbm), 1.0, 0.0, 1.0);
// Weather map coverage
float weather_scale = 0.00006;
vec2 weather_offset = vec2(wind_time * p.wind_time.z, wind_time * p.wind_time.w) * 0.003;
float weather_b = textureLod(weather_map, sample_pos.xz * weather_scale + 0.5 + weather_offset, 0.0).b;
float weather_coverage = coverage * weather_b;
base_cloud = remap(base_cloud * g, 1.0 - weather_coverage, 1.0, 0.0, 1.0);
base_cloud *= weather_coverage;
// Detail erosion (wind shear — detail scrolls faster)
vec3 detail_pos = world_pos;
detail_pos.xy -= 40.0 * wind_time;
vec3 hn = textureLod(worlnoise, detail_pos * 0.001, 0.0).rgb;
float hfbm = hn.r * 0.625 + hn.g * 0.25 + hn.b * 0.125;
hfbm = mix(hfbm, 1.0 - hfbm, clamp(height_frac * 4.0, 0.0, 1.0));
base_cloud = remap(base_cloud, hfbm * 0.2 * height_frac, 1.0, 0.0, 1.0);
float density = pow(clamp(base_cloud, 0.0, 1.0), (1.0 - height_frac) * 0.8 + 0.5);
// Edge fade to prevent hard box boundaries
vec3 edge = smoothstep(vec3(0.0), vec3(0.05), uvw) * smoothstep(vec3(0.0), vec3(0.05), vec3(1.0) - uvw);
density *= edge.x * edge.y * edge.z;
imageStore(density_out, gid, vec4(density, 0.0, 0.0, 0.0));
}