[0.13] An orthographic 2d Camera on top of a 3d Perspective Camera - Potential bug with Camera2d ClearColorConfig
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy version
0.13
Just in case it doesn't happen for someone - I don't think so, but better save than sorry:
```ignore
` 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`
```rust
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
// 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) {
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>,
mut materials: ResMut>,
) {
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`
```rust
[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:
```rust
//cmd.spawn((cam, UiCamera));
```
Then it looks like this:

The UI is rendered on top of the 3d camera which renders an unlighted Sphere.
Contributor guide
Research direction
Start by running the reproduction from main.rs with the Bevy 0.13 dependency in Cargo.toml, focusing on the Camera3d and Camera2d setup. Investigate how camera order and ClearColorConfig::None interact when both cameras render, then verify that the 3D scene remains visible behind the UI without the black background.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- game-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100