bevyengine / bevyengine/bevy

`VolumetricFog::ambient_intensity` has no visible effect in large/thick `FogVolume`s

Open
#25,283 1 comment 0 reactions 0 assignees View on GitHub
A-Rendering C-Bug D-Modest S-Ready-For-Implementation X-Uncontroversial
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):
Image

100x20x60 volume, ambient 8, the bug: fog against the sky stays black, only the short-path foreground responds, brightness falls with fog thickness:

Image

4x4x4 volume, ambient 8, control: at example scale the parameter works:
Image

100x20x60 volume, ambient 8, with the fix: brightness now increases with fog thickness (8 is deliberately extreme; calibrated values sit around 1-2):

Image

Contributor guide

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.