diff --git a/crates/benilla-world/src/shaders/sky_fx.wgsl b/crates/benilla-world/src/shaders/sky_fx.wgsl index e547f90b..251fcb04 100644 --- a/crates/benilla-world/src/shaders/sky_fx.wgsl +++ b/crates/benilla-world/src/shaders/sky_fx.wgsl @@ -7,6 +7,15 @@ #define_import_path benilla_world::sky_fx const TAU: f32 = 6.2831853; +// MONKEY (fix-sky): the sky clock's wrap (`sky_fx::SKY_CLOCK_WRAP_S`, a day). Twinkle rates are +// whole cycles per wrap, so the stars never jump at the wrap. +const SKY_WRAP: f32 = 86400.0; + +// The phase of a `rate` rad/s oscillator, rounded to whole cycles per wrap. +fn wrap_phase(t: f32, rate: f32) -> f32 { + let cycles = round(rate * SKY_WRAP / TAU); + return fract(cycles * (t / SKY_WRAP)) * TAU; +} fn srgb_to_linear(c: vec3) -> vec3 { let lo = c / 12.92; @@ -201,7 +210,8 @@ fn star_field(dir: vec3, t: f32, px: f32, band: f32) -> vec3 { let sigma = px * (0.55 + 0.7 * mag); let spot = exp(-d * d / (2.0 * sigma * sigma)); let tw_amp = 0.18 + 0.3 * (1.0 - clamp(dir.y, 0.0, 1.0)); - let tw = 1.0 + tw_amp * sin(t * (1.3 + 3.1 * j.x) + TAU * j.y) * sin(t * (0.6 + 1.7 * j.z) + TAU * h.z); + let tw = 1.0 + tw_amp * sin(wrap_phase(t, 1.3 + 3.1 * j.x) + TAU * j.y) + * sin(wrap_phase(t, 0.6 + 1.7 * j.z) + TAU * h.z); // Star colour: mostly white, a few warm and a few blue-white. let tint = mix(vec3(0.78, 0.86, 1.0), vec3(1.0, 0.88, 0.72), h.z); acc += tint * (b * spot * tw); diff --git a/crates/benilla-world/src/sky_fx.rs b/crates/benilla-world/src/sky_fx.rs index 3e5f7acd..e8462579 100644 --- a/crates/benilla-world/src/sky_fx.rs +++ b/crates/benilla-world/src/sky_fx.rs @@ -36,14 +36,23 @@ impl SkyQuality { } } -/// The sky's own animation clock (star twinkle, cloud detail drift), seconds wrapped to an hour so -/// the shader's `sin` keeps its precision. Frozen in capture mode, at `$WOW_CAPTURE_SKY_T` or 0. +/// The sky's own animation clock (star twinkle, cloud detail drift), seconds wrapped to a day. +/// Frozen in capture mode, at `$WOW_CAPTURE_SKY_T` or 0. +/// +/// MONKEY (fix-sky): it wrapped hourly in f32, and every star's twinkle jumped at the wrap. Now it +/// accumulates in f64 and wraps at [`SKY_CLOCK_WRAP_S`]; the shader's twinkle rates are whole +/// cycles per wrap (seamless). The High cloud-detail drift still re-patterns at the wrap, once +/// per 24 h of continuous play. #[derive(Resource, Default)] pub struct SkyClock { pub secs: f32, + acc: f64, frozen: Option, } +/// The sky clock's wrap in seconds; `sky_fx.wgsl`'s `SKY_WRAP` must match. +pub const SKY_CLOCK_WRAP_S: f64 = 86_400.0; + /// How strongly the glow shows: a broad halo at `GLOW_GAIN` × the sun colour at the sun itself. pub(crate) const GLOW_GAIN: f32 = 0.22; @@ -77,6 +86,7 @@ impl Plugin for SkyFxPlugin { }); app.insert_resource(SkyClock { secs: frozen.unwrap_or(0.0), + acc: 0.0, frozen, }); let quality = SkyQuality(SkyQuality::env_override().unwrap_or(0)); @@ -92,7 +102,8 @@ fn tick_sky_clock(time: Res