Hi dark_sylinc,
I’ve been integrating AtmosphereNpr with a continuous day/night cycle and ran into a reproducible colour flash (violet/blue) at dusk and dawn, even when time progression is smooth and monotonic.
After extensive debugging, this turns out to be an engine-side issue in AtmosphereNpr, not user code.
Symptoms
Smooth time progression (no jumps, no wrapping discontinuities)
Presets sorted and valid
Sun direction continuous
Still: one-frame blue/violet colour flash near horizon (≈18:00 / ≈06:00)
The flash happens even when:
timeOfDay is monotonic
preset interpolation is stable
no reset or teleport occurs
Root cause (confirmed in OgreAtmosphereNpr.cpp)
In AtmosphereNpr::updatePreset():
Code: Select all
mNormalizedTimeOfDay = std::min( fabsf( result.time ), 1.0f - 1e-6f );
This line destroys the sign of time.
As a result:
result.time = -0.98 (evening)
result.time = +0.98 (morning)
Both collapse to the same mNormalizedTimeOfDay = 0.98.
This causes a non-physical discontinuity in sun height, which then feeds into exponential scattering math:
Code: Select all
const float sunHeight = std::sin( mNormalizedTimeOfDay * Math::PI );
const float sunHeightWeight = std::exp2( -1.0f / sunHeight );
Near the horizon (sunHeight → 0), this produces a large Rayleigh spike → visible violet/blue flash.
This explains why:
time is smooth
yet colour spikes occur exactly at dusk/dawn
Why this is engine-side
User code can ensure smooth time, sorted presets, correct wrapping
But sign information is irreversibly lost inside AtmosphereNpr
There is no way to fix this externally without fighting the engine each frame
Suggested fix (minimal and safe)
Preserve the sign of time
Replace:
Code: Select all
mNormalizedTimeOfDay = std::min( fabsf( result.time ), 1.0f - 1e-6f );With:
Code: Select all
mNormalizedTimeOfDay = Math::Clamp( result.time, -1.0f + 1e-6f, 1.0f - 1e-6f); Avoid double-frequency sun motion
Currently sun height is computed as:
Code: Select all
sin( mNormalizedTimeOfDay * PI )For a signed [-1…1] time domain, this completes two half-cycles per day.
A physically consistent mapping would be:
Code: Select all
sin( (mNormalizedTimeOfDay * 0.5f) * PI )This produces one smooth day/night cycle without horizon spikes.
(Optional safety) Clamp sunHeight before division
There are divisions by sunHeight in multiple places.
Clamping once avoids numerical spikes:
const float sunHeightSafe = std::max( sunHeight, 0.01f );
Result after patch
Code: Select all
No colour flash at dusk or dawn
Continuous sky colour
Stable Rayleigh/Mie behaviour
Correct separation between morning and evening
Notes
AtmosphereNpr appears to have originally been designed for a [0…1] time domain and later extended to [-1…1], but the fabs() usage was never adjusted.
This bug only shows up when running a full continuous day/night cycle, which may explain why it went unnoticed.
Best Regars
Lax
