MONKEY (sky): SKY_FX / SKY_FX_CLOUDS pipeline keys so Classic compiles the reference shader unchanged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
pkuzic
2026-09-25 05:17:50 +01:00
co-authored by Claude Opus 5.5
parent 91ac4d1481
commit b11c1cfbbf
5 changed files with 62 additions and 10 deletions
+21 -1
View File
@@ -29,6 +29,7 @@ pub type CloudMaterial = ExtendedMaterial<StandardMaterial, CloudExt>;
/// 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<Self>,
key: MaterialExtensionKey<Self>,
) -> 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(())
}
+13 -6
View File
@@ -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<f32>;
@group(#{MATERIAL_BIND_GROUP}) @binding(101) var cloud_samp: sampler;
@@ -21,6 +23,7 @@ struct CloudFx {
};
@group(#{MATERIAL_BIND_GROUP}) @binding(102) var<uniform> cfx: CloudFx;
#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;
@@ -29,13 +32,16 @@ fn detailed_cover(uv: vec2<f32>, 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<f32> {
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<f32> {
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<f32>(1.0));
}
}
#endif
#ifdef VERTEX_COLORS
a *= in.color.a; // the dome's rim fade (ring alphas)
#endif
+6 -1
View File
@@ -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<f32>, // zenith (90°)
@@ -77,6 +80,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
// 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<vec3<f32>, 6>(
sky_fx::srgb_to_linear(sky.fog.rgb),
@@ -109,6 +113,7 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
let out = sky_fx::linear_to_srgb(lin) + vec3<f32>(sky_fx::dither_tri(in.position.xy));
return vec4<f32>(clamp(out, vec3<f32>(0.0), vec3<f32>(1.0)), 1.0);
}
#endif
// Elevation gradient, linear between rings like the reference's Gouraud-shaded dome.
var col: vec3<f32>;
+1 -1
View File
@@ -183,7 +183,7 @@ fn star_field(dir: vec3<f32>, t: f32, px: f32, band: f32) -> vec3<f32> {
let base = floor(p);
let f = p - base;
let o = select(vec3<f32>(-1.0), vec3<f32>(1.0), f > vec3<f32>(0.5));
let density = 0.035 * (1.0 + 2.5 * band);
let density = 0.022 * (1.0 + 2.5 * band);
var acc = vec3<f32>(0.0);
for (var i = 0u; i < 8u; i++) {
let off = vec3<f32>(f32(i & 1u), f32((i >> 1u) & 1u), f32((i >> 2u) & 1u)) * o;
+21 -1
View File
@@ -32,6 +32,7 @@ pub type SkyMaterial = ExtendedMaterial<StandardMaterial, SkyExt>;
/// 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<Self>,
key: MaterialExtensionKey<Self>,
) -> 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;
}