feat(weather): keep nebula dust clear of the play volume

The dust emitter's box fully encloses the arena, so motes drifted through the
pitch and hazed the surfaces the player is trying to read.

Fade each mote by the Chebyshev distance of its world-space origin outside
the play volume, taken from MODEL_MATRIX's translation column so it is
unaffected by the billboard rewrite of VERTEX. Motes inside the box are fully
hidden and fade in over arena_clear_distance beyond it, so the effect reads as
weather drifting around the station rather than through the match.

Extents default to ArenaBoundary's play volume and are exposed as uniforms.

Also adds the missing .uid sidecar for the shader.
This commit is contained in:
Josh Creek
2026-08-04 13:36:43 +01:00
parent e62d844c1d
commit a836a96d13
2 changed files with 18 additions and 1 deletions
+17 -1
View File
@@ -7,8 +7,16 @@ uniform vec3 core_bias_color : source_color = vec3(1.0, 0.6, 0.85);
uniform float core_bias_amount : hint_range(0.0, 1.0) = 0.35;
uniform float emission_strength : hint_range(0.0, 5.0) = 2.5;
uniform float soft_fade_distance : hint_range(0.0, 5.0) = 1.0;
// Arena play volume (world space) this weather effect must stay clear of —
// see ArenaBoundary.INNER_HALF_X/INNER_HALF_Z/INNER_HEIGHT. Dust is fully
// hidden inside that box and fades in over arena_clear_distance beyond it,
// so it reads as drifting around the station rather than through the pitch.
uniform vec3 arena_half_extents = vec3(12.0, 6.0, 18.0);
uniform vec3 arena_centre = vec3(0.0, 6.0, 0.0);
uniform float arena_clear_distance : hint_range(0.0, 10.0) = 2.0;
varying float v_flicker;
varying float v_arena_fade;
void vertex() {
// true billboard: strip rotation from the per-instance modelview, keep translation + scale
@@ -22,6 +30,14 @@ void vertex() {
float seed = INSTANCE_CUSTOM.x;
float flicker_hash = fract(sin((seed + floor(TIME * 6.0)) * 127.1) * 43758.5453);
v_flicker = 0.85 + 0.15 * flicker_hash;
// MODEL_MATRIX's translation column is this particle's world-space
// origin (pre-billboard, so unaffected by the VERTEX rewrite above).
// Chebyshev (box) distance outside arena_half_extents, in metres.
vec3 particle_pos = MODEL_MATRIX[3].xyz;
vec3 outside = abs(particle_pos - arena_centre) - arena_half_extents;
float outside_dist = max(max(outside.x, outside.y), outside.z);
v_arena_fade = smoothstep(0.0, arena_clear_distance, outside_dist);
}
void fragment() {
@@ -35,7 +51,7 @@ void fragment() {
vec4 tex = texture(albedo_tex, UV);
ALBEDO = tex.rgb * mix(vec3(1.0), core_bias_color, core_bias_amount);
EMISSION = core_bias_color * tex.rgb * emission_strength * 0.15;
ALPHA = tex.a * COLOR.a * mask;
ALPHA = tex.a * COLOR.a * mask * v_arena_fade;
float raw_depth = texture(depth_tex, SCREEN_UV).r;
vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, raw_depth);
+1
View File
@@ -0,0 +1 @@
uid://b6p22qfcd7qmt