Skip to content

Commit 838b318

Browse files
jakobhellermanncart
andcommitted
separate tonemapping and upscaling passes (#3425)
Attempt to make features like bloom #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>
1 parent 2023ce6 commit 838b318

File tree

36 files changed

+1142
-216
lines changed

36 files changed

+1142
-216
lines changed

crates/bevy_core_pipeline/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ webgl = []
1919
[dependencies]
2020
# bevy
2121
bevy_app = { path = "../bevy_app", version = "0.9.0-dev" }
22+
bevy_asset = { path = "../bevy_asset", version = "0.9.0-dev" }
2223
bevy_derive = { path = "../bevy_derive", version = "0.9.0-dev" }
2324
bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" }
2425
bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev" }
@@ -27,4 +28,5 @@ bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" }
2728
bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" }
2829

2930
serde = { version = "1", features = ["derive"] }
31+
bitflags = "1.2"
3032
radsort = "0.1"

crates/bevy_core_pipeline/src/core_2d/camera_2d.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::clear_color::ClearColorConfig;
1+
use crate::{clear_color::ClearColorConfig, tonemapping::Tonemapping};
22
use bevy_ecs::{prelude::*, query::QueryItem};
33
use bevy_reflect::Reflect;
44
use bevy_render::{
@@ -34,6 +34,7 @@ pub struct Camera2dBundle {
3434
pub transform: Transform,
3535
pub global_transform: GlobalTransform,
3636
pub camera_2d: Camera2d,
37+
pub tonemapping: Tonemapping,
3738
}
3839

3940
impl Default for Camera2dBundle {
@@ -74,6 +75,7 @@ impl Camera2dBundle {
7475
global_transform: Default::default(),
7576
camera: Camera::default(),
7677
camera_2d: Camera2d::default(),
78+
tonemapping: Tonemapping { is_enabled: false },
7779
}
7880
}
7981
}

crates/bevy_core_pipeline/src/core_2d/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub mod graph {
88
}
99
pub mod node {
1010
pub const MAIN_PASS: &str = "main_pass";
11+
pub const TONEMAPPING: &str = "tonemapping";
12+
pub const UPSCALING: &str = "upscaling";
1113
}
1214
}
1315

@@ -30,6 +32,8 @@ use bevy_render::{
3032
use bevy_utils::FloatOrd;
3133
use std::ops::Range;
3234

35+
use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
36+
3337
pub struct Core2dPlugin;
3438

3539
impl Plugin for Core2dPlugin {
@@ -52,10 +56,14 @@ impl Plugin for Core2dPlugin {
5256
);
5357

5458
let pass_node_2d = MainPass2dNode::new(&mut render_app.world);
59+
let tonemapping = TonemappingNode::new(&mut render_app.world);
60+
let upscaling = UpscalingNode::new(&mut render_app.world);
5561
let mut graph = render_app.world.resource_mut::<RenderGraph>();
5662

5763
let mut draw_2d_graph = RenderGraph::default();
5864
draw_2d_graph.add_node(graph::node::MAIN_PASS, pass_node_2d);
65+
draw_2d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
66+
draw_2d_graph.add_node(graph::node::UPSCALING, upscaling);
5967
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
6068
graph::input::VIEW_ENTITY,
6169
SlotType::Entity,
@@ -68,6 +76,28 @@ impl Plugin for Core2dPlugin {
6876
MainPass2dNode::IN_VIEW,
6977
)
7078
.unwrap();
79+
draw_2d_graph
80+
.add_slot_edge(
81+
input_node_id,
82+
graph::input::VIEW_ENTITY,
83+
graph::node::TONEMAPPING,
84+
TonemappingNode::IN_VIEW,
85+
)
86+
.unwrap();
87+
draw_2d_graph
88+
.add_slot_edge(
89+
input_node_id,
90+
graph::input::VIEW_ENTITY,
91+
graph::node::UPSCALING,
92+
UpscalingNode::IN_VIEW,
93+
)
94+
.unwrap();
95+
draw_2d_graph
96+
.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING)
97+
.unwrap();
98+
draw_2d_graph
99+
.add_node_edge(graph::node::TONEMAPPING, graph::node::UPSCALING)
100+
.unwrap();
71101
graph.add_sub_graph(graph::NAME, draw_2d_graph);
72102
}
73103
}

crates/bevy_core_pipeline/src/core_3d/camera_3d.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::clear_color::ClearColorConfig;
1+
use crate::{clear_color::ClearColorConfig, tonemapping::Tonemapping};
22
use bevy_ecs::{prelude::*, query::QueryItem};
33
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
44
use bevy_render::{
@@ -66,13 +66,15 @@ pub struct Camera3dBundle {
6666
pub transform: Transform,
6767
pub global_transform: GlobalTransform,
6868
pub camera_3d: Camera3d,
69+
pub tonemapping: Tonemapping,
6970
}
7071

7172
// NOTE: ideally Perspective and Orthographic defaults can share the same impl, but sadly it breaks rust's type inference
7273
impl Default for Camera3dBundle {
7374
fn default() -> Self {
7475
Self {
7576
camera_render_graph: CameraRenderGraph::new(crate::core_3d::graph::NAME),
77+
tonemapping: Tonemapping { is_enabled: true },
7678
camera: Default::default(),
7779
projection: Default::default(),
7880
visible_entities: Default::default(),

crates/bevy_core_pipeline/src/core_3d/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub mod graph {
88
}
99
pub mod node {
1010
pub const MAIN_PASS: &str = "main_pass";
11+
pub const TONEMAPPING: &str = "tonemapping";
12+
pub const UPSCALING: &str = "upscaling";
1113
}
1214
}
1315

@@ -38,6 +40,8 @@ use bevy_render::{
3840
};
3941
use bevy_utils::{FloatOrd, HashMap};
4042

43+
use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
44+
4145
pub struct Core3dPlugin;
4246

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

6468
let pass_node_3d = MainPass3dNode::new(&mut render_app.world);
69+
let tonemapping = TonemappingNode::new(&mut render_app.world);
70+
let upscaling = UpscalingNode::new(&mut render_app.world);
6571
let mut graph = render_app.world.resource_mut::<RenderGraph>();
6672

6773
let mut draw_3d_graph = RenderGraph::default();
6874
draw_3d_graph.add_node(graph::node::MAIN_PASS, pass_node_3d);
75+
draw_3d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
76+
draw_3d_graph.add_node(graph::node::UPSCALING, upscaling);
6977
let input_node_id = draw_3d_graph.set_input(vec![SlotInfo::new(
7078
graph::input::VIEW_ENTITY,
7179
SlotType::Entity,
@@ -78,6 +86,28 @@ impl Plugin for Core3dPlugin {
7886
MainPass3dNode::IN_VIEW,
7987
)
8088
.unwrap();
89+
draw_3d_graph
90+
.add_slot_edge(
91+
input_node_id,
92+
graph::input::VIEW_ENTITY,
93+
graph::node::TONEMAPPING,
94+
TonemappingNode::IN_VIEW,
95+
)
96+
.unwrap();
97+
draw_3d_graph
98+
.add_slot_edge(
99+
input_node_id,
100+
graph::input::VIEW_ENTITY,
101+
graph::node::UPSCALING,
102+
UpscalingNode::IN_VIEW,
103+
)
104+
.unwrap();
105+
draw_3d_graph
106+
.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING)
107+
.unwrap();
108+
draw_3d_graph
109+
.add_node_edge(graph::node::TONEMAPPING, graph::node::UPSCALING)
110+
.unwrap();
81111
graph.add_sub_graph(graph::NAME, draw_3d_graph);
82112
}
83113
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#define_import_path bevy_core_pipeline::fullscreen_vertex_shader
2+
3+
struct FullscreenVertexOutput {
4+
@builtin(position)
5+
position: vec4<f32>,
6+
@location(0)
7+
uv: vec2<f32>,
8+
};
9+
10+
@vertex
11+
fn fullscreen_vertex_shader(@builtin(vertex_index) vertex_index: u32) -> FullscreenVertexOutput {
12+
let uv = vec2<f32>(f32(vertex_index >> 1u), f32(vertex_index & 1u)) * 2.0;
13+
let clip_position = vec4<f32>(uv * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0), 0.0, 1.0);
14+
15+
return FullscreenVertexOutput(clip_position, uv);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use bevy_asset::HandleUntyped;
2+
use bevy_reflect::TypeUuid;
3+
use bevy_render::{prelude::Shader, render_resource::VertexState};
4+
5+
pub const FULLSCREEN_SHADER_HANDLE: HandleUntyped =
6+
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7837534426033940724);
7+
8+
/// uses the [`FULLSCREEN_SHADER_HANDLE`] to output a
9+
/// ```wgsl
10+
/// struct FullscreenVertexOutput {
11+
/// [[builtin(position)]]
12+
/// position: vec4<f32>;
13+
/// [[location(0)]]
14+
/// uv: vec2<f32>;
15+
/// };
16+
/// ```
17+
/// from the vertex shader.
18+
/// The draw call should render one triangle: `render_pass.draw(0..3, 0..1);`
19+
pub fn fullscreen_shader_vertex_state() -> VertexState {
20+
VertexState {
21+
shader: FULLSCREEN_SHADER_HANDLE.typed(),
22+
shader_defs: Vec::new(),
23+
entry_point: "fullscreen_vertex_shader".into(),
24+
buffers: Vec::new(),
25+
}
26+
}

crates/bevy_core_pipeline/src/lib.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
pub mod clear_color;
22
pub mod core_2d;
33
pub mod core_3d;
4+
pub mod fullscreen_vertex_shader;
5+
pub mod tonemapping;
6+
pub mod upscaling;
47

58
pub mod prelude {
69
#[doc(hidden)]
@@ -15,19 +18,32 @@ use crate::{
1518
clear_color::{ClearColor, ClearColorConfig},
1619
core_2d::Core2dPlugin,
1720
core_3d::Core3dPlugin,
21+
fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE,
22+
tonemapping::TonemappingPlugin,
23+
upscaling::UpscalingPlugin,
1824
};
1925
use bevy_app::{App, Plugin};
20-
use bevy_render::extract_resource::ExtractResourcePlugin;
26+
use bevy_asset::load_internal_asset;
27+
use bevy_render::{extract_resource::ExtractResourcePlugin, prelude::Shader};
2128

2229
#[derive(Default)]
2330
pub struct CorePipelinePlugin;
2431

2532
impl Plugin for CorePipelinePlugin {
2633
fn build(&self, app: &mut App) {
34+
load_internal_asset!(
35+
app,
36+
FULLSCREEN_SHADER_HANDLE,
37+
"fullscreen_vertex_shader/fullscreen.wgsl",
38+
Shader::from_wgsl
39+
);
40+
2741
app.register_type::<ClearColor>()
2842
.register_type::<ClearColorConfig>()
2943
.init_resource::<ClearColor>()
3044
.add_plugin(ExtractResourcePlugin::<ClearColor>::default())
45+
.add_plugin(TonemappingPlugin)
46+
.add_plugin(UpscalingPlugin)
3147
.add_plugin(Core2dPlugin)
3248
.add_plugin(Core3dPlugin);
3349
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#import bevy_core_pipeline::fullscreen_vertex_shader
2+
3+
@group(0) @binding(0)
4+
var texture: texture_2d<f32>;
5+
@group(0) @binding(1)
6+
var texture_sampler: sampler;
7+
8+
@fragment
9+
fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
10+
return textureSample(texture, texture_sampler, in.uv);
11+
}

0 commit comments

Comments
 (0)