Skip to content

Spotlight shadows inside volumetric fog incorrect #24232

@JeroenHoogers

Description

@JeroenHoogers

Bevy version and features

0.19-dev (main)

[Optional] Relevant system information

AdapterInfo { name: "NVIDIA GeForce RTX 3090", vendor: 4318, device: 8708, device_type: DiscreteGpu, device_pci_bus_id: "0000:09:00.0", driver: "NVIDIA", driver_info: "595.58.03", backend: Vulkan, subgroup_min_size: 32, subgroup_max_size: 32, transient_saves_memory: false }

What you did

Create a simple scene with a volumetric spotlight, pointlight, ground plane and sphere encapsulated in a fog volume.

//! Demonstrates volumetric fog and lighting (light shafts or god rays).
//! Note: On Wasm, this example only runs on WebGPU
#[cfg(feature = "free_camera")]
use bevy::camera_controller::free_camera::{FreeCamera, FreeCameraPlugin};
use bevy::{
    color::palettes::css::RED,
    core_pipeline::tonemapping::Tonemapping,
    light::{FogVolume, VolumetricFog, VolumetricLight},
    math::vec3,
    post_process::bloom::Bloom,
    prelude::*,
};

/// The current settings that the user has chosen.
#[derive(Resource)]
struct AppSettings {
    /// Whether volumetric spot light is on.
    volumetric_spotlight: bool,
    /// Whether volumetric point light is on.
    volumetric_pointlight: bool,
}

impl Default for AppSettings {
    fn default() -> Self {
        Self {
            volumetric_spotlight: true,
            volumetric_pointlight: true,
        }
    }
}

fn main() {
    App::new()
        .add_plugins((DefaultPlugins,
            #[cfg(feature = "free_camera")]
            FreeCameraPlugin))
        .insert_resource(ClearColor(Color::Srgba(Srgba {
            red: 0.02,
            green: 0.02,
            blue: 0.02,
            alpha: 1.0,
        })))
        .insert_resource(GlobalAmbientLight::NONE)
        .init_resource::<AppSettings>()
        .add_systems(Startup, setup)
        // .add_systems(Update, adjust_app_settings)
        .run();
}

/// Initializes the scene.
fn setup(mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>) {
    // Spawn the camera.
    commands
        .spawn((
            Camera3d::default(),
            Transform::from_xyz(-10.0, 0.0, 0.0).looking_at(vec3(0.0, 0.0, 0.0), Vec3::Y),
            Tonemapping::TonyMcMapface,
	        #[cfg(feature = "free_camera")]
	        FreeCamera::default(),
            Bloom::default(),
        ))
        .insert(VolumetricFog {
            ambient_intensity: 0.0,
            ..default()
        });
    let material_red = materials.add(StandardMaterial {
        base_color: Color::srgb_u8(255, 50, 50),
        ..default()
    });

    let material_white = materials.add(StandardMaterial {
        base_color: Color::WHITE,
        ..default()
    });

    commands.spawn((
	    Transform::from_xyz(0.0, 0.0, 0.0),
	    Mesh3d(meshes.add(Sphere::new(1.0))),
	    MeshMaterial3d(material_red)
	));

    commands.spawn((
	    Transform::from_xyz(0.0, -1.0, 0.0),
	    Mesh3d(meshes.add(Plane3d::new(vec3(0.0, 1.0, 0.0), vec2(10.0, 10.0)))),
	    MeshMaterial3d(material_white)
	));

    commands.spawn((
        Transform::from_xyz(0.4, 1.9, -1.0),
        PointLight {
            shadow_maps_enabled: true,
            range: 150.0,
            color: RED.into(),
            intensity: 50_000.0,
            ..default()
        },
        VolumetricLight,
    ));

    // commands.spawn((
    //     // Transform::from_rotation(),
    //     Transform::from_xyz(-1.8, 3.9, -2.7).looking_at(Vec3::ZERO, Vec3::Y),
    //     DirectionalLight {
    //         shadow_maps_enabled: true,
    //         ..default()
    //     },
    //     VolumetricLight,
    // ));

    // Add the spot light
    commands.spawn((
        Transform::from_xyz(-1.8, 3.9, -2.7).looking_at(Vec3::ZERO, Vec3::Y),
        SpotLight {
            intensity: 150_000.0, // lumens
            color: Color::WHITE,
            shadow_maps_enabled: true,
            inner_angle: 0.76,
            outer_angle: 0.94,
            ..default()
        },
        VolumetricLight,
    ));

    // Add the fog volume.
    commands.spawn((
        FogVolume::default(),
        Transform::from_scale(Vec3::splat(15.0)),
    ));
}

What went wrong

The spot light's volumetric shadow is behaving incorrectly. Note how the white light of the spotlight bleeds through the ground plane while the red light of the pointlight does not:

Image

Same scene from another angle: Look at how the volumetric fog of the spotlight is cut-off at a weird angle, this doesn't happen for the point light. I know that this cutoff is in fact the shadow cast by the plane since it goes away when I remove the plane.
Image

When the spot light is pointed straight-down the issues go away:

Image

This makes me think the plane's orientation is assumed to be in the local coordinate space of the spotlight instead of in world-space.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-BugAn unexpected or incorrect behaviorS-Needs-TriageThis issue needs to be labelled

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions