OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / addons / nubis_clouds / shaders / cloud_density_fill.glsl

87 lines · glsl ref:
#[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));
}