[0.13] An orthographic 2d Camera on top of a 3d Perspective Camera - Potential bug with Camera2d ClearColorConfig
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
- 3d perspective camera for the actual world
- 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:
If I do NOT spawn the camera2d:
//cmd.spawn((cam, UiCamera));
Then it looks like this:
The UI is rendered on top of the 3d camera which renders an unlighted Sphere.
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:
Without UI camera spawned:
Potentially related: https://discord.com/channels/691052431525675048/1212672226449555486/1212672226449555486 (frustum of the second camera seems to override the visibility set by the first one)