Skip to content

Commit

Permalink
separate tonemapping and upscaling passes (bevyengine#3425)
Browse files Browse the repository at this point in the history
Attempt to make features like bloom bevyengine#2876 easier to implement.

**This PR:**
- Moves the tonemapping from `pbr.wgsl` into a separate pass
- also add a separate upscaling pass after the tonemapping which writes to the swap chain (enables resolution-independant rendering and post-processing after tonemapping)
- adds a `hdr` bool to the camera which controls whether the pbr and sprite shaders render into a `Rgba16Float` texture

**Open questions:**
- ~should the 2d graph work the same as the 3d one?~ it is the same now
- ~The current solution is a bit inflexible because while you can add a post processing pass that writes to e.g. the `hdr_texture`, you can't write to a separate `user_postprocess_texture` while reading the `hdr_texture` and tell the tone mapping pass to read from the `user_postprocess_texture` instead. If the tonemapping and upscaling render graph nodes were to take in a `TextureView` instead of the view entity this would almost work, but the bind groups for their respective input textures are already created in the `Queue` render stage in the hardcoded order.~ solved by creating bind groups in render node

**New render graph:**

![render_graph](https://user-images.githubusercontent.com/22177966/147767249-57dd4229-cfab-4ec5-9bf3-dc76dccf8e8b.png)
<details>
<summary>Before</summary>

![render_graph_old](https://user-images.githubusercontent.com/22177966/147284579-c895fdbd-4028-41cf-914c-e1ffef60e44e.png)
</details>

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
  • Loading branch information
2 people authored and ItsDoot committed Feb 1, 2023
1 parent b9e8521 commit 28c544e
Show file tree
Hide file tree
Showing 36 changed files with 1,142 additions and 216 deletions.
2 changes: 2 additions & 0 deletions crates/bevy_core_pipeline/Cargo.toml
Expand Up @@ -19,6 +19,7 @@ webgl = []
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.9.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.9.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.9.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev" }
Expand All @@ -27,4 +28,5 @@ bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" }

serde = { version = "1", features = ["derive"] }
bitflags = "1.2"
radsort = "0.1"
4 changes: 3 additions & 1 deletion crates/bevy_core_pipeline/src/core_2d/camera_2d.rs
@@ -1,4 +1,4 @@
use crate::clear_color::ClearColorConfig;
use crate::{clear_color::ClearColorConfig, tonemapping::Tonemapping};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_reflect::Reflect;
use bevy_render::{
Expand Down Expand Up @@ -34,6 +34,7 @@ pub struct Camera2dBundle {
pub transform: Transform,
pub global_transform: GlobalTransform,
pub camera_2d: Camera2d,
pub tonemapping: Tonemapping,
}

impl Default for Camera2dBundle {
Expand Down Expand Up @@ -74,6 +75,7 @@ impl Camera2dBundle {
global_transform: Default::default(),
camera: Camera::default(),
camera_2d: Camera2d::default(),
tonemapping: Tonemapping { is_enabled: false },
}
}
}
30 changes: 30 additions & 0 deletions crates/bevy_core_pipeline/src/core_2d/mod.rs
Expand Up @@ -8,6 +8,8 @@ pub mod graph {
}
pub mod node {
pub const MAIN_PASS: &str = "main_pass";
pub const TONEMAPPING: &str = "tonemapping";
pub const UPSCALING: &str = "upscaling";
}
}

Expand All @@ -30,6 +32,8 @@ use bevy_render::{
use bevy_utils::FloatOrd;
use std::ops::Range;

use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};

pub struct Core2dPlugin;

impl Plugin for Core2dPlugin {
Expand All @@ -52,10 +56,14 @@ impl Plugin for Core2dPlugin {
);

let pass_node_2d = MainPass2dNode::new(&mut render_app.world);
let tonemapping = TonemappingNode::new(&mut render_app.world);
let upscaling = UpscalingNode::new(&mut render_app.world);
let mut graph = render_app.world.resource_mut::<RenderGraph>();

let mut draw_2d_graph = RenderGraph::default();
draw_2d_graph.add_node(graph::node::MAIN_PASS, pass_node_2d);
draw_2d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
draw_2d_graph.add_node(graph::node::UPSCALING, upscaling);
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
Expand All @@ -68,6 +76,28 @@ impl Plugin for Core2dPlugin {
MainPass2dNode::IN_VIEW,
)
.unwrap();
draw_2d_graph
.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::TONEMAPPING,
TonemappingNode::IN_VIEW,
)
.unwrap();
draw_2d_graph
.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::UPSCALING,
UpscalingNode::IN_VIEW,
)
.unwrap();
draw_2d_graph
.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING)
.unwrap();
draw_2d_graph
.add_node_edge(graph::node::TONEMAPPING, graph::node::UPSCALING)
.unwrap();
graph.add_sub_graph(graph::NAME, draw_2d_graph);
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_core_pipeline/src/core_3d/camera_3d.rs
@@ -1,4 +1,4 @@
use crate::clear_color::ClearColorConfig;
use crate::{clear_color::ClearColorConfig, tonemapping::Tonemapping};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::{
Expand Down Expand Up @@ -66,13 +66,15 @@ pub struct Camera3dBundle {
pub transform: Transform,
pub global_transform: GlobalTransform,
pub camera_3d: Camera3d,
pub tonemapping: Tonemapping,
}

// NOTE: ideally Perspective and Orthographic defaults can share the same impl, but sadly it breaks rust's type inference
impl Default for Camera3dBundle {
fn default() -> Self {
Self {
camera_render_graph: CameraRenderGraph::new(crate::core_3d::graph::NAME),
tonemapping: Tonemapping { is_enabled: true },
camera: Default::default(),
projection: Default::default(),
visible_entities: Default::default(),
Expand Down
30 changes: 30 additions & 0 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Expand Up @@ -8,6 +8,8 @@ pub mod graph {
}
pub mod node {
pub const MAIN_PASS: &str = "main_pass";
pub const TONEMAPPING: &str = "tonemapping";
pub const UPSCALING: &str = "upscaling";
}
}

Expand Down Expand Up @@ -38,6 +40,8 @@ use bevy_render::{
};
use bevy_utils::{FloatOrd, HashMap};

use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};

pub struct Core3dPlugin;

impl Plugin for Core3dPlugin {
Expand All @@ -62,10 +66,14 @@ impl Plugin for Core3dPlugin {
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent3d>);

let pass_node_3d = MainPass3dNode::new(&mut render_app.world);
let tonemapping = TonemappingNode::new(&mut render_app.world);
let upscaling = UpscalingNode::new(&mut render_app.world);
let mut graph = render_app.world.resource_mut::<RenderGraph>();

let mut draw_3d_graph = RenderGraph::default();
draw_3d_graph.add_node(graph::node::MAIN_PASS, pass_node_3d);
draw_3d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
draw_3d_graph.add_node(graph::node::UPSCALING, upscaling);
let input_node_id = draw_3d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
Expand All @@ -78,6 +86,28 @@ impl Plugin for Core3dPlugin {
MainPass3dNode::IN_VIEW,
)
.unwrap();
draw_3d_graph
.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::TONEMAPPING,
TonemappingNode::IN_VIEW,
)
.unwrap();
draw_3d_graph
.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::UPSCALING,
UpscalingNode::IN_VIEW,
)
.unwrap();
draw_3d_graph
.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING)
.unwrap();
draw_3d_graph
.add_node_edge(graph::node::TONEMAPPING, graph::node::UPSCALING)
.unwrap();
graph.add_sub_graph(graph::NAME, draw_3d_graph);
}
}
Expand Down
@@ -0,0 +1,16 @@
#define_import_path bevy_core_pipeline::fullscreen_vertex_shader

struct FullscreenVertexOutput {
@builtin(position)
position: vec4<f32>,
@location(0)
uv: vec2<f32>,
};

@vertex
fn fullscreen_vertex_shader(@builtin(vertex_index) vertex_index: u32) -> FullscreenVertexOutput {
let uv = vec2<f32>(f32(vertex_index >> 1u), f32(vertex_index & 1u)) * 2.0;
let clip_position = vec4<f32>(uv * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0), 0.0, 1.0);

return FullscreenVertexOutput(clip_position, uv);
}
26 changes: 26 additions & 0 deletions crates/bevy_core_pipeline/src/fullscreen_vertex_shader/mod.rs
@@ -0,0 +1,26 @@
use bevy_asset::HandleUntyped;
use bevy_reflect::TypeUuid;
use bevy_render::{prelude::Shader, render_resource::VertexState};

pub const FULLSCREEN_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7837534426033940724);

/// uses the [`FULLSCREEN_SHADER_HANDLE`] to output a
/// ```wgsl
/// struct FullscreenVertexOutput {
/// [[builtin(position)]]
/// position: vec4<f32>;
/// [[location(0)]]
/// uv: vec2<f32>;
/// };
/// ```
/// from the vertex shader.
/// The draw call should render one triangle: `render_pass.draw(0..3, 0..1);`
pub fn fullscreen_shader_vertex_state() -> VertexState {
VertexState {
shader: FULLSCREEN_SHADER_HANDLE.typed(),
shader_defs: Vec::new(),
entry_point: "fullscreen_vertex_shader".into(),
buffers: Vec::new(),
}
}
18 changes: 17 additions & 1 deletion crates/bevy_core_pipeline/src/lib.rs
@@ -1,6 +1,9 @@
pub mod clear_color;
pub mod core_2d;
pub mod core_3d;
pub mod fullscreen_vertex_shader;
pub mod tonemapping;
pub mod upscaling;

pub mod prelude {
#[doc(hidden)]
Expand All @@ -15,19 +18,32 @@ use crate::{
clear_color::{ClearColor, ClearColorConfig},
core_2d::Core2dPlugin,
core_3d::Core3dPlugin,
fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE,
tonemapping::TonemappingPlugin,
upscaling::UpscalingPlugin,
};
use bevy_app::{App, Plugin};
use bevy_render::extract_resource::ExtractResourcePlugin;
use bevy_asset::load_internal_asset;
use bevy_render::{extract_resource::ExtractResourcePlugin, prelude::Shader};

#[derive(Default)]
pub struct CorePipelinePlugin;

impl Plugin for CorePipelinePlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(
app,
FULLSCREEN_SHADER_HANDLE,
"fullscreen_vertex_shader/fullscreen.wgsl",
Shader::from_wgsl
);

app.register_type::<ClearColor>()
.register_type::<ClearColorConfig>()
.init_resource::<ClearColor>()
.add_plugin(ExtractResourcePlugin::<ClearColor>::default())
.add_plugin(TonemappingPlugin)
.add_plugin(UpscalingPlugin)
.add_plugin(Core2dPlugin)
.add_plugin(Core3dPlugin);
}
Expand Down
11 changes: 11 additions & 0 deletions crates/bevy_core_pipeline/src/tonemapping/blit.wgsl
@@ -0,0 +1,11 @@
#import bevy_core_pipeline::fullscreen_vertex_shader

@group(0) @binding(0)
var texture: texture_2d<f32>;
@group(0) @binding(1)
var texture_sampler: sampler;

@fragment
fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
return textureSample(texture, texture_sampler, in.uv);
}

0 comments on commit 28c544e

Please sign in to comment.