From f5c58c81a2eef2b304e364f67047cb92c1152a76 Mon Sep 17 00:00:00 2001
From: pkuzic
Date: Sat, 26 Sep 2026 06:00:26 +0100
Subject: [PATCH] MONKEY (visualfix): volumetric fog density follows the
rendered clock
The dawn/weather density curve read WorldTime, the server clock, which stays at its noon default
offline, so a manual or capture time never got the dawn mist. It now reads the rendered minute
through grading's rendered_minute (the same rule as ac9a1ef3). Baselines move: volfog-dawn is
mistier (MAE 8.5), volfog-night is unchanged (0.07).
Co-Authored-By: Claude Opus 5.5
---
LIGHTING.md | 2 +-
crates/benilla-app/src/post/grading.rs | 2 +-
crates/benilla-app/src/post/mod.rs | 3 ++-
crates/benilla-app/src/volumetric_fog.rs | 8 +++++++-
4 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/LIGHTING.md b/LIGHTING.md
index 2b40ac08..651e4aa7 100644
--- a/LIGHTING.md
+++ b/LIGHTING.md
@@ -25,7 +25,7 @@ behind each constant.
| Spell and firework lights | fire, holy and fel effects light their surroundings for their lifetime; frost, nature, arcane and shadow do not | `spellLightGain`, env `WOW_SPELL_LIGHT=0` |
| Moon shadows | at night the same shadow rig re-aims at the moon and casts a faint shadow; dims only the night sky term, never point lights | `moonShadowStrength` |
| Ground-effect spells | Flamestrike, Rain of Fire, Consecration, Flare and fire traps light the ground for their duration; frost and nature areas stay dark | `spellLightGain` |
-| Volumetric fog | near-field haze that converges on the zone fog colour (clear within 10 yd, full by 150 yd; mistier at dawn and in bad weather, faint indoors) and sun/moon light shafts through gaps, sampled from the shadow map; own fullscreen pass after the main pass (`benilla-app/src/volumetric_fog.rs`) | Advanced Graphics → Volumetric Fog (Off/Low/High), cvar `volumetricFog`, env `WOW_VOLFOG=0\|1\|2` |
+| Volumetric fog | near-field haze that converges on the zone fog colour (clear within 10 yd, full by 150 yd; mistier at dawn and in bad weather, faint indoors; the dawn curve follows the rendered clock, so a manual or capture time sets it; over an ocean a point below sea level is hazed at the sea surface, since water writes no depth) and sun/moon light shafts through gaps, sampled from the shadow map; own fullscreen pass after the main pass (`benilla-app/src/volumetric_fog.rs`) | Advanced Graphics → Volumetric Fog (Off/Low/High), cvar `volumetricFog`, env `WOW_VOLFOG=0\|1\|2` |
| Sky dithering | a faint screen-space dither in the FFXGlow combine so smooth sky and fog gradients do not band (MONKEY p0; was env-only) | Advanced Graphics → Sky Dithering, cvar `skyDither` 0/1 (default 0, Graphics preset High = 1), env `WOW_DITHER=1` still forces it on; bridge in `benilla-app/src/monkey_gfx.rs` |
| HDR emission and bloom | lit WMO windows, additive models/particles and magma can exceed display white; a soft-capped fullscreen halo is added before the faithful FFX clamp | Advanced Graphics → Bloom (Off/Low/High), cvar `bloom`; Off is the unchanged reference image, suggested High preset value 2 |
| Sky quality | Enhanced: the five Light.dbc sky stops through a smooth monotone curve in linear light (no bands at the rings), a soft sun glow tinted by sun and fog colour (fades at night and under cloud), a procedural star field with twinkle and a faint Milky Way over the stock `Stars.m2`. High adds domain-warped cloud detail and sun-lit clouds (self-shadow, silver lining; technique from WarcraftXL, see `THIRD-PARTY.md`). Classic is the reference sky unchanged | Advanced Graphics → Sky Quality, cvar `skyQuality` 0 Classic / 1 Enhanced / 2 High, env `WOW_SKY_QUALITY` |
diff --git a/crates/benilla-app/src/post/grading.rs b/crates/benilla-app/src/post/grading.rs
index 82048096..c5ead119 100644
--- a/crates/benilla-app/src/post/grading.rs
+++ b/crates/benilla-app/src/post/grading.rs
@@ -362,7 +362,7 @@ fn update_views(
/// MONKEY (reviewfix-a): the server's fractional minute while it is the minute being rendered,
/// else the rendered (manual or capture) minute.
-fn rendered_minute(time: &WorldTime, rendered: Option<&benilla_world::lighting::GameClock>) -> f32 {
+pub(crate) fn rendered_minute(time: &WorldTime, rendered: Option<&benilla_world::lighting::GameClock>) -> f32 {
match rendered {
Some(c) if time.minute_f.floor() as u32 != c.minute => c.minute as f32,
_ => time.minute_f,
diff --git a/crates/benilla-app/src/post/mod.rs b/crates/benilla-app/src/post/mod.rs
index 2952e5a6..b25cbc26 100644
--- a/crates/benilla-app/src/post/mod.rs
+++ b/crates/benilla-app/src/post/mod.rs
@@ -1,7 +1,8 @@
//! MONKEY (post): optional world-camera effects ahead of FFXGlow's gamma clamp.
mod bloom;
-mod grading;
+// MONKEY (visualfix): crate-visible for `rendered_minute` (volumetric fog).
+pub(crate) mod grading;
mod sun_shafts;
pub(crate) struct PostPlugin;
diff --git a/crates/benilla-app/src/volumetric_fog.rs b/crates/benilla-app/src/volumetric_fog.rs
index 374b99f5..56d540e2 100644
--- a/crates/benilla-app/src/volumetric_fog.rs
+++ b/crates/benilla-app/src/volumetric_fog.rs
@@ -241,6 +241,8 @@ fn update_fog(
interior: Res,
// MONKEY (lampfog): the resolved point-table view published by global_light's packer.
point_lights: Res,
+ // MONKEY (visualfix): the minute the lighting rendered (manual / capture clock offline).
+ rendered: Option>,
// MONKEY (fog): the Modern fog rows (Option: absent in unit tests).
monkey: Option>,
mut cameras: Query<
@@ -331,7 +333,11 @@ fn update_fog(
lamp_positions: lamps.positions,
lamp_colors: lamps.colors,
color_density: fog.extend(
- density(clock.minute_f, weather_amount, interior.0.is_some())
+ density(
+ crate::post::grading::rendered_minute(&clock, rendered.as_deref()),
+ weather_amount,
+ interior.0.is_some(),
+ )
* if tier == 0 {
0.0
} else if tier == 2 {