Files
benilla-octo/crates/benilla-world/src/shaders/cloud.wgsl
T
Claude 1f094e7880 MONKEY (polish): the sky clock leaves the sky and cloud materials
At skyQuality >= 1 the dome and cloud materials were rewritten every frame
only because `fx.y` carried the sky clock, so each frame re-prepared both
material bind groups. The clock now lives in `SkyClockBuffer`, a 16-byte
STORAGE | COPY_DST buffer created beside the shared light buffer
(AssetSet::Open) and bound read-only by both materials (sky binding 101,
cloud binding 103), the same shape as the materials' shared `light_buf`.
The render world writes `SkyClock::secs` into it in PrepareResources each
frame. `fx.y` is now reserved (0), so `write_gated` only touches a material
when a real input moves. The clock is still our own f64 accumulator with
the seamless 86400 s wrap (Bevy's globals.time wraps hourly); capture mode
still freezes it. A headless build with no RenderDevice gets no buffer and
spawns no sky or cloud dome.

Verified: cargo check -p benilla; cargo test -p benilla-world --lib (643
passed, 8 ignored); sky.wgsl (Classic and SKY_FX) and cloud.wgsl (plain and
SKY_FX_CLOUDS + VERTEX_COLORS) compose and validate under naga_oil 0.20 /
naga 27 offline against stubbed bevy_pbr imports and the real fog_hook,
monkey_frame and sky_fx modules. No GPU or WoW data here; built on Linux only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y6yUWgPosSakvL23gtgvpH
2026-09-25 19:02:03 +00:00

75 lines
3.2 KiB
WebGPU Shading Language
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The cloud dome. Its colour is CPU-side as in the reference: the kernel's `0x6cfb00` port builds
// the image the reference binds as its texture (`0x58ac70`). This stage applies the dome's rim
// fade (`0x6d0530`) and blends the raw gamma texels premultiplied. Depth is the far pin in
// `sky_vertex.wgsl`.
//
// MONKEY (sky): at `skyQuality` 2 the texels get GPU detail and a short sun march.
// Ported from WarcraftXL (https://github.com/WarcraftXL) by iThorgrim — module wxl-retail-clouds, Clouds.cpp.
#import bevy_pbr::forward_io::VertexOutput
#ifdef SKY_FX_CLOUDS
#import benilla_world::sky_fx
#endif
@group(#{MATERIAL_BIND_GROUP}) @binding(100) var cloud_tex: texture_2d<f32>;
@group(#{MATERIAL_BIND_GROUP}) @binding(101) var cloud_samp: sampler;
// MONKEY (sky): `fx.x` the sky tier, `.y` reserved, `.zw` the unit direction toward the
// glow body on the sheet (u = world x, v = world z); `lit.rgb` the Light.dbc cloud sun colour
// (gamma), `.w` the march strength (the body's flatness × the glow envelope).
struct CloudFx {
fx: vec4<f32>,
lit: vec4<f32>,
};
@group(#{MATERIAL_BIND_GROUP}) @binding(102) var<uniform> cfx: CloudFx;
// MONKEY (polish): x = the sky clock (s, wrapped at a day), written per frame outside the material.
@group(#{MATERIAL_BIND_GROUP}) @binding(103) var<storage, read> sky_clock: vec4<f32>;
#ifdef SKY_FX_CLOUDS
// MONKEY (sky): coverage with detail at a sheet point; `oct` detail octaves.
fn detailed_cover(uv: vec2<f32>, oct: i32) -> f32 {
let a = textureSampleLevel(cloud_tex, cloud_samp, uv, 0.0).a;
if (a <= 0.0) {
return 0.0;
}
return sky_fx::cloud_erode(a, sky_fx::cloud_detail(uv, sky_clock.x, oct));
}
#endif
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
let texel = textureSample(cloud_tex, cloud_samp, in.uv);
var a = texel.a;
var rgb = texel.rgb;
// MONKEY (sky): High only (the `SKY_FX_CLOUDS` pipeline); Classic and Enhanced keep the
// kernel's texels as they are.
#ifdef SKY_FX_CLOUDS
if (cfx.fx.x >= 1.5) {
let n = sky_fx::cloud_detail(in.uv, sky_clock.x, 4);
a = sky_fx::cloud_erode(a, n);
// A painterly tone inside the mass from the same detail.
rgb = rgb * (0.93 + 0.14 * n);
let strength = cfx.lit.w;
if (a > 0.003 && strength > 0.0) {
let sd = cfx.fx.zw;
let stp = 0.011;
var occl = detailed_cover(in.uv + sd * stp, 2);
occl += detailed_cover(in.uv + sd * stp * 2.0, 2) * 0.75;
occl += detailed_cover(in.uv + sd * stp * 3.5, 2) * 0.5;
let transmit = exp(-occl * 1.44 * strength);
// WarcraftXL's ambient floor 0.55, softened toward the painted texel.
let light = 0.62 + 0.38 * transmit;
let ahead = detailed_cover(in.uv + sd * stp * 1.5, 2);
let rim = max(ahead - a, 0.0) * (1.0 - a) * transmit * strength;
rgb = rgb * mix(1.0, light, strength) + cfx.lit.rgb * rim * 0.9;
rgb = min(rgb, vec3<f32>(1.0));
}
}
#endif
#ifdef VERTEX_COLORS
a *= in.color.a; // the dome's rim fade (ring alphas)
#endif
// Premultiplied gamma blend; the RGB is already the reference's byte math.
return vec4<f32>(rgb * a, a);
}