bevyengine / bevyengine/bevy

Volumetric point/spot light in-scatter vanishes in large `FogVolume`s

Open
#25,282 1 comment 0 reactions 0 assignees View on GitHub
A-Rendering C-Bug S-Ready-For-Implementation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## Bevy version and features

- v0.19.0 (default features, Linux x86_64, Vulkan)

## What you did

Built a foggy street scene: a world-scale `FogVolume` with street lamps and headlights as volumetric point/spot lights. Minimal form (ambient at 0 so the point light is the only fog illumination):

```rust
commands.spawn((
Camera3d::default(),
Hdr,
VolumetricFog {
ambient_intensity: 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 volume...
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 same light below produces
// the expected glow ball.

// Shadow-mapped volumetric point light inside the fog.
commands.spawn((
PointLight {
intensity: 300_000.0,
range: 40.0,
shadow_maps_enabled: true,
..default()
},
VolumetricLight,
Transform::from_xyz(0.0, 2.5, 0.0),
));
```

Full runnable reproduction:
https://github.com/blaind/bevy-volumetric-ambient-repro (`MODE=pointlight`)

## What went wrong

- Expected: a glow ball around the light in both volume sizes.
- Actually: the 4x4x4 volume shows the expected glow; the 100x20x60 volume crushes the same light, same position, same intensity, to a barely visible pinprick.

Cause, in `crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl` (applied per raymarch step in the point/spot loop, and in the directional loop too):

```wgsl
let light_attenuation = exp(-density * bounding_radius * (absorption + scattering));
```

`bounding_radius` is the radius of the sphere bounding the **entire fog volume**, a constant. Every light at every sample is charged the cost of crossing the whole volume regardless of the actual light-to-sample distance, and the local sample density is extrapolated over that constant distance. For the large volume here: density 0.15 x radius ~59 x extinction 0.65 gives `exp(-5.75)`, 0.3% of the light's scatter. The falloff is exponential in volume size, so realistic-lumen punctual lights die completely at world scale; directional god rays survive in practice only because tens of thousands of lux have headroom.

## Additional information

- Suggested fix included in the repro as [`fix-punctual-light-fog-attenuation.patch`](https://github.com/blaind/bevy-volumetric-ambient-repro/blob/main/fix-punctual-light-fog-attenuation.patch)

4-unit volume: the expected glow ball:
Image

100x20x60 volume, same light: the glow is crushed to a pinprick by the bounding-radius term:
Image

same large volume with the fix: the light-to-sample distance replaces the bounding radius, and the glow returns
Image

fix regression check: the small volume still renders a comparable glow ball:
Image

Contributor guide

Open the contributing guide

Research direction

Start in crates/bevy_pbr/src/volumetric_fog/volumetric_fog.wgsl and inspect the point/spot-light attenuation in the raymarch loop. Run the linked reproduction with MODE=pointlight, comparing the small and world-scale FogVolumes and the included fix-punctual-light-fog-attenuation.patch. Done means the large volume restores the point-light glow while the small volume remains comparable.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
computer-graphics, game-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.