From b11c1cfbbfd729a216bd0074ab65a6d6eee6aee7 Mon Sep 17 00:00:00 2001 From: pkuzic Date: Fri, 25 Sep 2026 05:17:50 +0100 Subject: [PATCH] MONKEY (sky): SKY_FX / SKY_FX_CLOUDS pipeline keys so Classic compiles the reference shader unchanged Co-Authored-By: Claude Opus 5.5 --- crates/benilla-world/src/clouds/layer.rs | 22 +++++++++++++++++++- crates/benilla-world/src/shaders/cloud.wgsl | 19 +++++++++++------ crates/benilla-world/src/shaders/sky.wgsl | 7 ++++++- crates/benilla-world/src/shaders/sky_fx.wgsl | 2 +- crates/benilla-world/src/sky.rs | 22 +++++++++++++++++++- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/crates/benilla-world/src/clouds/layer.rs b/crates/benilla-world/src/clouds/layer.rs index b76e8024..fc495faf 100644 --- a/crates/benilla-world/src/clouds/layer.rs +++ b/crates/benilla-world/src/clouds/layer.rs @@ -29,6 +29,7 @@ pub type CloudMaterial = ExtendedMaterial; /// The colored cloud texture. The color math is CPU-side as in the reference, so the texels are /// raw gamma bytes in a non-sRGB texture. #[derive(Asset, AsBindGroup, Clone, TypePath)] +#[bind_group_data(CloudFxKey)] pub struct CloudExt { /// The colored tile, alpha the coverage, re-uploaded each regen (`0x58ac70`). #[texture(100)] @@ -43,6 +44,19 @@ pub struct CloudExt { pub(crate) lit: Vec4, } +/// MONKEY (sky): the pipeline key: High compiles `SKY_FX_CLOUDS` into `cloud.wgsl`; below High +/// the reference's code runs with nothing added. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct CloudFxKey { + high: bool, +} + +impl From<&CloudExt> for CloudFxKey { + fn from(e: &CloudExt) -> Self { + Self { high: e.fx.x >= 1.5 } + } +} + impl MaterialExtension for CloudExt { /// The shared sky vertex stage, which pins every sky vertex to the far depth. fn vertex_shader() -> ShaderRef { @@ -57,8 +71,14 @@ impl MaterialExtension for CloudExt { _pipeline: &MaterialExtensionPipeline, descriptor: &mut RenderPipelineDescriptor, _layout: &MeshVertexBufferLayoutRef, - _key: MaterialExtensionKey, + key: MaterialExtensionKey, ) -> Result<(), SpecializedMeshPipelineError> { + // MONKEY (sky): the High cloud branch. + if key.bind_group_data.high { + if let Some(fragment) = descriptor.fragment.as_mut() { + fragment.shader_defs.push("SKY_FX_CLOUDS".into()); + } + } sky_pipeline_state(descriptor); Ok(()) } diff --git a/crates/benilla-world/src/shaders/cloud.wgsl b/crates/benilla-world/src/shaders/cloud.wgsl index 91b18485..d2413618 100644 --- a/crates/benilla-world/src/shaders/cloud.wgsl +++ b/crates/benilla-world/src/shaders/cloud.wgsl @@ -7,7 +7,9 @@ // 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; @group(#{MATERIAL_BIND_GROUP}) @binding(101) var cloud_samp: sampler; @@ -21,6 +23,7 @@ struct CloudFx { }; @group(#{MATERIAL_BIND_GROUP}) @binding(102) var cfx: CloudFx; +#ifdef SKY_FX_CLOUDS // MONKEY (sky): coverage with detail at a sheet point; `oct` detail octaves. fn detailed_cover(uv: vec2, oct: i32) -> f32 { let a = textureSampleLevel(cloud_tex, cloud_samp, uv, 0.0).a; @@ -29,13 +32,16 @@ fn detailed_cover(uv: vec2, oct: i32) -> f32 { } return sky_fx::cloud_erode(a, sky_fx::cloud_detail(uv, cfx.fx.y, oct)); } +#endif @fragment fn fragment(in: VertexOutput) -> @location(0) vec4 { let texel = textureSample(cloud_tex, cloud_samp, in.uv); var a = texel.a; var rgb = texel.rgb; - // MONKEY (sky): High only; Classic and Enhanced keep the kernel's texels as they are. + // 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, cfx.fx.y, 4); a = sky_fx::cloud_erode(a, n); @@ -44,19 +50,20 @@ fn fragment(in: VertexOutput) -> @location(0) vec4 { let strength = cfx.lit.w; if (a > 0.003 && strength > 0.0) { let sd = cfx.fx.zw; - let step = 0.011; - var occl = detailed_cover(in.uv + sd * step, 2); - occl += detailed_cover(in.uv + sd * step * 2.0, 2) * 0.75; - occl += detailed_cover(in.uv + sd * step * 3.5, 2) * 0.5; + 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 * step * 1.5, 2); + 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(1.0)); } } +#endif #ifdef VERTEX_COLORS a *= in.color.a; // the dome's rim fade (ring alphas) #endif diff --git a/crates/benilla-world/src/shaders/sky.wgsl b/crates/benilla-world/src/shaders/sky.wgsl index c1f371f6..702421b5 100644 --- a/crates/benilla-world/src/shaders/sky.wgsl +++ b/crates/benilla-world/src/shaders/sky.wgsl @@ -5,8 +5,11 @@ forward_io::VertexOutput, mesh_view_bindings::view, } -// MONKEY (sky): the Enhanced/High library (smooth gradient, sun glow, night sky). +// MONKEY (sky): the Enhanced/High library (smooth gradient, sun glow, night sky), compiled only +// into the `SKY_FX` pipeline so Classic keeps the reference's exact code. +#ifdef SKY_FX #import benilla_world::sky_fx +#endif struct SkyColors { sky0: vec4, // zenith (90°) @@ -77,6 +80,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4 { // MONKEY (sky): Enhanced/High. The same stops through a monotone cubic in linear light, then // the sun glow and the night sky added in linear light, back to gamma, dithered. +#ifdef SKY_FX if (sky.fx.x >= 0.5) { let stops = array, 6>( sky_fx::srgb_to_linear(sky.fog.rgb), @@ -109,6 +113,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4 { let out = sky_fx::linear_to_srgb(lin) + vec3(sky_fx::dither_tri(in.position.xy)); return vec4(clamp(out, vec3(0.0), vec3(1.0)), 1.0); } +#endif // Elevation gradient, linear between rings like the reference's Gouraud-shaded dome. var col: vec3; diff --git a/crates/benilla-world/src/shaders/sky_fx.wgsl b/crates/benilla-world/src/shaders/sky_fx.wgsl index ed74f870..e22f3356 100644 --- a/crates/benilla-world/src/shaders/sky_fx.wgsl +++ b/crates/benilla-world/src/shaders/sky_fx.wgsl @@ -183,7 +183,7 @@ fn star_field(dir: vec3, t: f32, px: f32, band: f32) -> vec3 { let base = floor(p); let f = p - base; let o = select(vec3(-1.0), vec3(1.0), f > vec3(0.5)); - let density = 0.035 * (1.0 + 2.5 * band); + let density = 0.022 * (1.0 + 2.5 * band); var acc = vec3(0.0); for (var i = 0u; i < 8u; i++) { let off = vec3(f32(i & 1u), f32((i >> 1u) & 1u), f32((i >> 2u) & 1u)) * o; diff --git a/crates/benilla-world/src/sky.rs b/crates/benilla-world/src/sky.rs index 661cca7d..a0fef04a 100644 --- a/crates/benilla-world/src/sky.rs +++ b/crates/benilla-world/src/sky.rs @@ -32,6 +32,7 @@ pub type SkyMaterial = ExtendedMaterial; /// The five sky stops, zenith to horizon, and the fog colour on one binding (100); `sky.wgsl`'s /// struct must match this field order. #[derive(Asset, AsBindGroup, Clone, TypePath)] +#[bind_group_data(SkyFxKey)] pub struct SkyExt { /// `SkyColor0`: zenith (90°). #[uniform(100)] @@ -67,6 +68,19 @@ pub struct SkyExt { pub(crate) glow: Vec4, } +/// MONKEY (sky): the pipeline key: Enhanced/High compile `SKY_FX` into `sky.wgsl`; Classic keeps +/// the reference's code with nothing added. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct SkyFxKey { + fx: bool, +} + +impl From<&SkyExt> for SkyFxKey { + fn from(e: &SkyExt) -> Self { + Self { fx: e.fx.x >= 0.5 } + } +} + impl MaterialExtension for SkyExt { /// The shared sky vertex stage, which pins depth to the far plane ([`crate::sky_order`]). fn vertex_shader() -> ShaderRef { @@ -83,8 +97,14 @@ impl MaterialExtension for SkyExt { _pipeline: &MaterialExtensionPipeline, descriptor: &mut RenderPipelineDescriptor, _layout: &MeshVertexBufferLayoutRef, - _key: MaterialExtensionKey, + key: MaterialExtensionKey, ) -> Result<(), SpecializedMeshPipelineError> { + // MONKEY (sky): the Enhanced/High branch. + if key.bind_group_data.fx { + if let Some(fragment) = descriptor.fragment.as_mut() { + fragment.shader_defs.push("SKY_FX".into()); + } + } if let Some(depth) = descriptor.depth_stencil.as_mut() { depth.depth_write_enabled = false; }