From f28b92120920f387020f3b3e858f0e7039b9c07e Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 7 Jun 2022 22:22:10 +0000 Subject: [PATCH] Add "depth_load_op" configuration to 3d Cameras (#4904) # Objective Users should be able to configure depth load operations on cameras. Currently every camera clears depth when it is rendered. But sometimes later passes need to rely on depth from previous passes. ## Solution This adds the `Camera3d::depth_load_op` field with a new `Camera3dDepthLoadOp` value. This is a custom type because Camera3d uses "reverse-z depth" and this helps us record and document that in a discoverable way. It also gives us more control over reflection + other trait impls, whereas `LoadOp` is owned by the `wgpu` crate. ```rust commands.spawn_bundle(Camera3dBundle { camera_3d: Camera3d { depth_load_op: Camera3dDepthLoadOp::Load, ..default() }, ..default() }); ``` ### two_passes example with the "second pass" camera configured to the default (clear depth to 0.0) ![image](https://user-images.githubusercontent.com/2694663/171743172-46d4fdd5-5090-46ea-abe4-1fbc519f6ee8.png) ### two_passes example with the "second pass" camera configured to "load" the depth ![image](https://user-images.githubusercontent.com/2694663/171743323-74dd9a1d-9c25-4883-98dd-38ca0bed8c17.png) --- ## Changelog ### Added * `Camera3d` now has a `depth_load_op` field, which can configure the Camera's main 3d pass depth loading behavior. --- .../src/core_3d/camera_3d.rs | 36 +++++++++++++++++-- .../src/core_3d/main_pass_3d_node.rs | 4 +-- examples/3d/render_to_texture.rs | 1 + examples/3d/split_screen.rs | 1 + examples/3d/two_passes.rs | 1 + examples/shader/post_processing.rs | 1 + 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs index a04a3df38d..a9bc38464f 100644 --- a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs +++ b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs @@ -1,18 +1,50 @@ use crate::clear_color::ClearColorConfig; use bevy_ecs::{prelude::*, query::QueryItem}; -use bevy_reflect::Reflect; +use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_render::{ camera::{Camera, CameraRenderGraph, Projection}, extract_component::ExtractComponent, primitives::Frustum, + render_resource::LoadOp, view::VisibleEntities, }; use bevy_transform::prelude::{GlobalTransform, Transform}; +use serde::{Deserialize, Serialize}; -#[derive(Component, Default, Reflect, Clone)] +/// Configuration for the "main 3d render graph". +#[derive(Component, Reflect, Clone, Default)] #[reflect(Component)] pub struct Camera3d { + /// The clear color operation to perform for the main 3d pass. pub clear_color: ClearColorConfig, + /// The depth clear operation to perform for the main 3d pass. + pub depth_load_op: Camera3dDepthLoadOp, +} + +/// The depth clear operation to perform for the main 3d pass. +#[derive(Reflect, Serialize, Deserialize, Clone, Debug)] +#[reflect_value(Serialize, Deserialize)] +pub enum Camera3dDepthLoadOp { + /// Clear with a specified value. + /// Note that 0.0 is the far plane due to bevy's use of reverse-z projections. + Clear(f32), + /// Load from memory. + Load, +} + +impl Default for Camera3dDepthLoadOp { + fn default() -> Self { + Camera3dDepthLoadOp::Clear(0.0) + } +} + +impl From for LoadOp { + fn from(config: Camera3dDepthLoadOp) -> Self { + match config { + Camera3dDepthLoadOp::Clear(x) => LoadOp::Clear(x), + Camera3dDepthLoadOp::Load => LoadOp::Load, + } + } } impl ExtractComponent for Camera3d { diff --git a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs index 9e1de353b7..1521e230a9 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs @@ -87,8 +87,8 @@ impl Node for MainPass3dNode { view: &depth.view, // NOTE: The opaque main pass loads the depth buffer and possibly overwrites it depth_ops: Some(Operations { - // NOTE: 0.0 is the far plane due to bevy's use of reverse-z projections - load: LoadOp::Clear(0.0), + // NOTE: 0.0 is the far plane due to bevy's use of reverse-z projections. + load: camera_3d.depth_load_op.clone().into(), store: true, }), stencil_ops: None, diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 76a88a2935..f9179d49b9 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -95,6 +95,7 @@ fn setup( .spawn_bundle(Camera3dBundle { camera_3d: Camera3d { clear_color: ClearColorConfig::Custom(Color::WHITE), + ..default() }, camera: Camera { // render before the "main pass" camera diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index b296f170dd..1b0992c84b 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -66,6 +66,7 @@ fn setup( camera_3d: Camera3d { // dont clear on the second camera because the first camera already cleared the window clear_color: ClearColorConfig::None, + ..default() }, ..default() }) diff --git a/examples/3d/two_passes.rs b/examples/3d/two_passes.rs index 1937a55581..af83417a12 100644 --- a/examples/3d/two_passes.rs +++ b/examples/3d/two_passes.rs @@ -49,6 +49,7 @@ fn setup( transform: Transform::from_xyz(10.0, 10., -5.0).looking_at(Vec3::ZERO, Vec3::Y), camera_3d: Camera3d { clear_color: ClearColorConfig::None, + ..default() }, camera: Camera { // renders after / on top of the main camera diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index 14ec05ba77..e0e8a1608c 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -102,6 +102,7 @@ fn setup( commands.spawn_bundle(Camera3dBundle { camera_3d: Camera3d { clear_color: ClearColorConfig::Custom(Color::WHITE), + ..default() }, camera: Camera { target: RenderTarget::Image(image_handle.clone()),