Skip to content

VolumetricFog::ambient_intensity has no visible effect in large/thick FogVolumes #25283

Description

@blaind

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:

// 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:

// 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: 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behaviorD-ModestA "normal" level of difficulty; suitable for simple features or challenging fixesS-Ready-For-ImplementationThis issue is ready for an implementation PR. Go for it!X-UncontroversialThis work is generally agreed upon

    Type

    No type

    Projects

    Status
    Needs SME Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions