bevyengine / bevyengine/bevy

`FogVolume`s silently render nothing when no `VolumetricLight` exists in the scene

Open
#25,284 2 comments 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

Tried to build ambient-only fog (overcast sky, no god rays): a `FogVolume`, a camera with `VolumetricFog`, and a directional light without the `VolumetricLight` marker:

```rust
commands.spawn((
Camera3d::default(),
Hdr,
VolumetricFog {
ambient_intensity: 8.0,
..default()
},
Transform::from_xyz(0.0, 4.0, 16.0).looking_at(Vec3::new(0.0, 3.0, 0.0), Vec3::Y),
));

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)),
));

// Note: no VolumetricLight anywhere in the scene.
commands.spawn((
DirectionalLight {
illuminance: 12000.0,
shadow_maps_enabled: false,
..default()
},
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 (`MODE=nolight`)

## What went wrong

- Expected: fog lit by the ambient term, per the `FogVolume::fog_color` docs: "Note that the fog must be lit by a `VolumetricLight` **or ambient light** in order for this color to appear." At minimum, extinction.
- Actually: nothing renders at all. No in-scatter, no extinction. The screenshot is **byte-identical** (same sha256) to a render without working fog.

The gate is explicit in `crates/bevy_pbr/src/volumetric_fog/render.rs`, `extract_volumetric_fog`:

```rust
if volumetric_lights.is_empty() {
// TODO: needs better way to handle clean up in render world
for (entity, ..) in view_targets.iter() {
commands.entity(entity)
.remove::<(VolumetricFog, ViewVolumetricFogPipelines, ViewVolumetricFog)>();
}
for (entity, ..) in fog_volumes.iter() {
commands.entity(entity).remove::();
}
return;
}
```

So ambient-only fog cannot be expressed at all. The workaround is attaching `VolumetricLight` to a light that contributes nothing (e.g. a shadowless directional light), purely to activate the pass.

Possibly a design decision rather than a plain bug, but at least one of these seems warranted:

1. Run the pass when a `FogVolume` and a `VolumetricFog` camera exist, even with zero `VolumetricLight`s (extinction and ambient still apply), or
2. Fix the docs: `FogVolume::fog_color` currently promises ambient-only lighting works, and the `VolumetricFog` camera component doc (the natural docs.rs entry point) mentions neither the `FogVolume` nor the `VolumetricLight` requirement (it does document the WebGPU-on-Wasm requirement).

Contributor guide

Open the contributing guide

Research direction

Start in crates/bevy_pbr/src/volumetric_fog/render.rs at extract_volumetric_fog and inspect the early return when volumetric_lights is empty. Use the linked ambient-only reproduction with MODE=nolight to compare behavior. Done means ambient-only FogVolume rendering follows the chosen behavior, including either working fog or corrected documentation.

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
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.