`VolumetricFog::ambient_intensity` has no visible effect in large/thick `FogVolume`s
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## Bevy version and features
- v0.19.0 (default features, Linux x86_64, Vulkan)
## What you did
Tried to brighten a world-scale fog bank (tens of units deep) with the camera's `VolumetricFog::ambient_intensity`, the parameter the docs designate for ambient/skylight in-scatter:
```rust
// Camera with ambient fog in-scatter.
commands.spawn((
Camera3d::default(),
Hdr,
VolumetricFog {
ambient_intensity: 8.0, // <- has no visible effect on the fog bank
step_count: 64,
jitter: 0.0,
..default()
},
Transform::from_xyz(0.0, 4.0, 16.0).looking_at(Vec3::new(0.0, 3.0, 0.0), Vec3::Y),
));
// A world-scale fog bank: rays toward the sky cross ~60 units of fog.
commands.spawn((
FogVolume {
density_factor: 0.15,
scattering: 0.6,
absorption: 0.05,
..default()
},
Transform::from_xyz(0.0, 5.0, -22.0).with_scale(Vec3::new(100.0, 20.0, 60.0)),
));
// ...swap the scale for Vec3::splat(4.0) and the parameter works as expected.
// VolumetricLight is required for the pass to run at all; with shadow maps
// disabled the directional light contributes zero in-scatter, so the ambient
// term is the only possible fog illumination in this scene.
commands.spawn((
DirectionalLight {
illuminance: 12000.0,
shadow_maps_enabled: false,
..default()
},
VolumetricLight,
Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, -1.0, 0.4, 0.0)),
));
```
Full runnable reproduction:
https://github.com/blaind/bevy-volumetric-ambient-repro (`VOLUME=large|small AMBIENT=0|8`)
## What went wrong
- Expected: raising `ambient_intensity` brightens the fog, most visibly where the fog is thickest.
- Actually: the parameter is numerically dead where it matters. Measured max per-pixel delta between ambient 0 and ambient 8 in the upper half of the frame (fog against the sky, ~60 units of fog along the ray): **10/255**, mean 0.13. Only short rays to nearby geometry respond (max 122/255 in the foreground), so fog brightness *decreases* with thickness, which is physically inverted. Shrinking the same volume to 4x4x4 (the regime of the shipped examples) makes the parameter work fine (max 138/255), which is presumably why this went unnoticed.
Cause, the closed-form ambient term in `crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl`:
```wgsl
// Use Beer's law again to accumulate the ambient light all along the path.
var accumulated_color = exp(-ray_length_view * (absorption + scattering)) * ambient_color *
ambient_intensity;
```
It applies extinction over the entire in-fog ray length at implicit density 1.0 (`density_factor` and the density texture are never consulted for ambient), so for a 60-unit path with `absorption + scattering = 0.65` the factor is `exp(-39)`, i.e. zero for any `ambient_intensity`. The comment says "accumulate the ambient light all along the path", but the code models only light entering at the far end of the ray.
## Additional information
- Suggested code for investigating fix (**degrades performance** - should not be merged as is) included in the repro as [`fix-volumetric-ambient-in-scatter.patch`](https://github.com/blaind/bevy-volumetric-ambient-repro/blob/main/fix-volumetric-ambient-in-scatter.patch): initialize the accumulator to zero and integrate ambient per step, mirroring the existing per-light march. Verified: bit-exact no-op at intensity 0, monotonic response, identical with shadows on/off.
100x20x60 volume, ambient 0, baseline (extinction only):
100x20x60 volume, ambient 8, the bug: fog against the sky stays black, only the short-path foreground responds, brightness falls with fog thickness:
4x4x4 volume, ambient 8, control: at example scale the parameter works:
100x20x60 volume, ambient 8, with the fix: brightness now increases with fog thickness (8 is deliberately extreme; calibrated values sit around 1-2):
Contributor guide
Research direction
Start with crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl and compare the ambient term with the existing per-light march. Run the linked reproduction in its large and small volume modes with AMBIENT=0 and AMBIENT=8. Done means ambient intensity visibly responds through thick fog, remains a no-op at zero, behaves monotonically, and does not regress shadowed or unshadowed cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- computer-graphics, game-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 64/100