bevy icon indicating copy to clipboard operation
bevy copied to clipboard

[0.13] An orthographic 2d Camera on top of a 3d Perspective Camera - Potential bug with Camera2d ClearColorConfig

Open nerdachse opened this issue 6 months ago • 2 comments

Bevy version

0.13

Just in case it doesn't happen for someone - I don't think so, but better save than sorry:

` bevy_render::renderer: AdapterInfo { name: "AMD Radeon RX 6700 XT (RADV NAVI22)", vendor: 4098, device: 29663, device_type: DiscreteGpu, driver: "radv", driver_info: "Mesa 24.0.1-arch1.1", backend: Vulkan }`

What you did

I want to render a menu on top of a "scene" (not a bevy scene) - Right now that's for an "interactive" main-menu... I want a background "video" (actually just a small example world rendered where the camera moves around)

I want to add TWO cameras

  1. 3d perspective camera for the actual world
  2. 2d orthographic camera for the UI

I want to have that distinction - especially since I want to add some specific cursor-images to my HUD and it's way easier with viewport_to_world_2d.

main.rs


use bevy::{prelude::*, window::WindowResolution};

fn main() {
    App::new()
        .add_plugins(
            DefaultPlugins
                .set(ImagePlugin::default_nearest())
                .set(WindowPlugin {
                    primary_window: Some(Window {
                        resolution: WindowResolution::new(1920.0, 1080.0),
                        title: "Just a test".to_owned(),
                        ..default()
                    }),
                    ..default()
                }),
        )
        .add_plugins((
            camera::CameraPlugin,
            mainmenu::MainMenuPlugin,
            world::WorldPlugin,
        ))
        //.insert_resource(Msaa::Off)
        .run();
}

mod camera {
    use bevy::{core_pipeline::bloom::BloomSettings, prelude::*};

    #[derive(Component)]
    pub struct MainCamera;

    #[derive(Component)]
    pub struct UiCamera;

    pub struct CameraPlugin;

    impl Plugin for CameraPlugin {
        fn build(&self, app: &mut App) {
            app.add_systems(Startup, setup)
                .add_systems(Update, move_camera_around_origin);
        }
    }

    fn setup(mut cmd: Commands) {
        info!("Setting up cameras!");
        // 3d Main Camera
        cmd.spawn((
        Camera3dBundle {
            camera: Camera {
                hdr: true,
                clear_color: ClearColorConfig::Custom(Color::BLACK),
                ..default()
            },
            projection: bevy::prelude::Projection::Perspective(PerspectiveProjection {
                fov: std::f32::consts::PI / 6.0,
                ..default()
            }),
            ..default()
        },
        BloomSettings {
            intensity: 0.3,
            ..default()
        },
        MainCamera,
        ))
        //.insert(ScreenSpaceAmbientOcclusionBundle::default())
        //.insert(TemporalAntiAliasBundle::default())
        ;

          // UI
          let mut cam = Camera2dBundle::default();
          cam.camera.order = 2; // On top of the 3d "background" camera
          cam.camera.clear_color = ClearColorConfig::None; //::Custom(Color::NONE);
          cmd.spawn((cam, UiCamera));
    }

    fn move_camera_around_origin(
        time: Res<Time>,
        mut query: Query<&mut Transform, With<MainCamera>>,
    ) {
        // parameters for how the camera orbits the area
        const CAM_DISTANCE: f32 = 25.0;
        const CAM_HEIGHT: f32 = 16.0;
        const CAM_SPEED: f32 = -0.05;

        // camera will always orbit 0,0,0, but can look somewhere slightly different
        const CAM_TARGET_X: f32 = 2.0;
        const CAM_TARGET_Z: f32 = -5.5;

        const CAM_T_OFFSET: f32 = -0.4;
        let mut transform = query.single_mut();
        let time = std::f32::consts::PI - time.elapsed_seconds() * CAM_SPEED + CAM_T_OFFSET;
        transform.translation.x = time.sin() * CAM_DISTANCE;
        transform.translation.y = CAM_HEIGHT;
        transform.translation.z = time.cos() * CAM_DISTANCE;
        transform.look_at(Vec3::new(CAM_TARGET_X, 0.0, CAM_TARGET_Z), Vec3::Y);
    }
}

mod mainmenu {
    use bevy::prelude::*;

    pub struct MainMenuPlugin;

    impl Plugin for MainMenuPlugin {
        fn build(&self, app: &mut App) {
            app.add_systems(Startup, setup);
        }
    }

    fn setup(mut cmd: Commands, ass: Res<AssetServer>) {
        let image = ass.load("ui/panel-border-030.png");

        let slicer = TextureSlicer {
            border: BorderRect::square(22.0),
            center_scale_mode: SliceScaleMode::Stretch,
            sides_scale_mode: SliceScaleMode::Stretch,
            max_corner_scale: 1.0,
        };

        cmd.spawn((NodeBundle {
            style: Style {
                width: Val::Percent(100.0),
                height: Val::Percent(100.0),
                align_items: AlignItems::Center,
                justify_content: JustifyContent::Center,
                ..default()
            },
            ..default()
        },))
            .with_children(|parent| {
                parent
                    .spawn((
                        ButtonBundle {
                            style: Style {
                                width: Val::Px(300.0),
                                height: Val::Px(100.0),
                                // horizontally center child text
                                justify_content: JustifyContent::Center,
                                // vertically center child text
                                align_items: AlignItems::Center,
                                margin: UiRect::all(Val::Px(20.0)),
                                ..default()
                            },
                            image: image.clone().into(),
                            ..default()
                        },
                        ImageScaleMode::Sliced(slicer.clone()),
                    ))
                    .with_children(|parent| {
                        parent.spawn(TextBundle::from_section(
                            "Play",
                            TextStyle {
                                font: ass.load("fonts/FiraSans-Bold.ttf"),
                                font_size: 40.0,
                                color: Color::rgb(0.9, 0.9, 0.9),
                            },
                        ));
                    });
            });
    }
}

mod world {
    use bevy::prelude::*;

    pub struct WorldPlugin;
    impl Plugin for WorldPlugin {
        fn build(&self, app: &mut App) {
            app.add_systems(Startup, setup);
        }
    }

    fn setup(
        mut cmd: Commands,
        mut meshes: ResMut<Assets<Mesh>>,
        mut materials: ResMut<Assets<StandardMaterial>>,
    ) {
        cmd.spawn((PbrBundle {
            mesh: meshes.add(Sphere::new(0.6)),
            material: materials.add(Color::WHITE),
            transform: Transform::from_xyz(-0.9, 0.5, -4.2),
            ..default()
        },));
    }
}

Cargo.toml

[package]
name = "bevy_bug_camera_ui"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bevy = { version = "0.13" }

What went wrong

The problem: When I add the Camera2d with order: 2 (so it renders on top of the Camera3d) and clear_color = ClearColorConfig::None; there is a black background:

screenshot_26 02 2024-12 08 27

If I do NOT spawn the camera2d:

        //cmd.spawn((cam, UiCamera));

Then it looks like this:

screenshot_26 02 2024-12 09 17

The UI is rendered on top of the 3d camera which renders an unlighted Sphere.

nerdachse avatar Feb 26 '24 11:02 nerdachse

I am sorry, here is a better example, that doesn't rely on any image for the button:


    fn setup(mut cmd: Commands, ass: Res<AssetServer>) {
        cmd.spawn((NodeBundle {
            style: Style {
                width: Val::Percent(100.0),
                height: Val::Percent(100.0),
                align_items: AlignItems::Center,
                justify_content: JustifyContent::Center,
                ..default()
            },
            ..default()
        },))
            .with_children(|parent| {
                parent
                    .spawn((ButtonBundle {
                        style: Style {
                            width: Val::Px(300.0),
                            height: Val::Px(100.0),
                            // horizontally center child text
                            justify_content: JustifyContent::Center,
                            // vertically center child text
                            align_items: AlignItems::Center,
                            margin: UiRect::all(Val::Px(20.0)),
                            ..default()
                        },
                        ..default()
                    },))
                    .with_children(|parent| {
                        parent.spawn(TextBundle::from_section(
                            "Play",
                            TextStyle {
                                font: ass.load("fonts/FiraSans-Bold.ttf"),
                                font_size: 40.0,
                                color: Color::rgb(0.9, 0.9, 0.9),
                            },
                        ));
                    });
            });
    }

With UI camera spawned:

screenshot_26 02 2024-12 14 45

Without UI camera spawned:

screenshot_26 02 2024-12 15 14

nerdachse avatar Feb 26 '24 11:02 nerdachse

Potentially related: https://discord.com/channels/691052431525675048/1212672226449555486/1212672226449555486 (frustum of the second camera seems to override the visibility set by the first one)

Selene-Amanita avatar Feb 29 '24 12:02 Selene-Amanita