Skip to content

Directional lights ignore walls on WebGPU when the camera is too parallel to them #19626

Open
@janhohenheim

Description

@janhohenheim

Bevy version

0.16.2

Relevant system information

Using Google Chrome: Version 137.0.7151.68 (Official Build) (64-bit)

AdapterInfo { name: "", vendor: 0, device: 0, device_type: Other, driver: "", driver_info: "", backend: BrowserWebGpu }

Note this only happens on WebGPU

What you did

use bevy::{color::palettes::tailwind, prelude::*};

fn main() -> AppExit {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, rotate_camera)
        .run()
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // Light
    commands.spawn((
        DirectionalLight {
            illuminance: 5_000.0,
            shadows_enabled: true,
            ..default()
        },
        Transform::from_xyz(0.0, 5.0, -8.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    // Ground
    commands.spawn((
        Transform::from_xyz(0.0, 0.0, 0.0),
        Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(10.0)))),
        MeshMaterial3d(materials.add(Color::from(tailwind::RED_400))),
    ));

    // Left Cover
    commands.spawn((
        Transform::from_xyz(-2.0, 2.0, -4.0),
        Mesh3d(meshes.add(Cuboid::new(2.0, 5.0, 0.5))),
        MeshMaterial3d(materials.add(Color::from(tailwind::RED_400))),
    ));

    // Right Cover
    commands.spawn((
        Transform::from_xyz(2.0, 2.0, -4.0),
        Mesh3d(meshes.add(Cuboid::new(2.0, 5.0, 0.5))),
        MeshMaterial3d(materials.add(Color::from(tailwind::RED_400))),
    ));

    // Upper Cover
    commands.spawn((
        Transform::from_xyz(0.0, 4.0, -4.0),
        Mesh3d(meshes.add(Cuboid::new(5.0, 2.0, 0.5))),
        MeshMaterial3d(materials.add(Color::from(tailwind::RED_400))),
    ));

    // Camera
    commands.spawn((Camera3d::default(), Transform::from_xyz(0.0, 1.0, 0.0)));
}

fn rotate_camera(mut camera: Single<&mut Transform, With<Camera>>, time: Res<Time>) {
    camera.rotate_y(time.delta_secs() * 0.5);
}

What went wrong

native:

Screencast.From.2025-06-14.20-03-23.mp4

web:

Screencast.From.2025-06-14.20-01-43.mp4

The issue gets worse with increased shadow cascades, but it's hard to notice on the minimal example. So let's look at Chainboom.

In Chainboom using the default shadow cascade config (4 I believe):

Screencast_From_2025-06-06_03-21-46.mp4

In Chainboom using only a single shadow cascade:

Screencast_From_2025-06-06_03-39-03.mp4

Additional information

Our workaround for Chainboom was to simply disable the directional light if it would start causing trouble, as a workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behaviorO-WebSpecific to web (WASM) buildsO-WebGPUSpecific to the WebGPU render APIS-Needs-InvestigationThis issue requires detective work to figure out what's going wrong

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions