mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
separate tonemapping and upscaling passes (#3425)
Attempt to make features like bloom https://github.com/bevyengine/bevy/pull/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>
This commit is contained in:
parent
2023ce63c7
commit
838b318863
36 changed files with 1143 additions and 217 deletions
|
@ -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" }
|
||||
|
@ -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"
|
||||
|
|
|
@ -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::{
|
||||
|
@ -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 {
|
||||
|
@ -74,6 +75,7 @@ impl Camera2dBundle {
|
|||
global_transform: Default::default(),
|
||||
camera: Camera::default(),
|
||||
camera_2d: Camera2d::default(),
|
||||
tonemapping: Tonemapping { is_enabled: false },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::{
|
||||
|
@ -66,6 +66,7 @@ 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
|
||||
|
@ -73,6 +74,7 @@ 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(),
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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(),
|
||||
}
|
||||
}
|
|
@ -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)]
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
11
crates/bevy_core_pipeline/src/tonemapping/blit.wgsl
Normal file
11
crates/bevy_core_pipeline/src/tonemapping/blit.wgsl
Normal file
|
@ -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);
|
||||
}
|
145
crates/bevy_core_pipeline/src/tonemapping/mod.rs
Normal file
145
crates/bevy_core_pipeline/src/tonemapping/mod.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
mod node;
|
||||
|
||||
use bevy_ecs::query::QueryItem;
|
||||
use bevy_render::camera::Camera;
|
||||
use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin};
|
||||
pub use node::TonemappingNode;
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{load_internal_asset, HandleUntyped};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::renderer::RenderDevice;
|
||||
use bevy_render::texture::BevyDefault;
|
||||
use bevy_render::{render_resource::*, RenderApp};
|
||||
|
||||
use bevy_reflect::TypeUuid;
|
||||
|
||||
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
|
||||
|
||||
const TONEMAPPING_SHADER_HANDLE: HandleUntyped =
|
||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 17015368199668024512);
|
||||
|
||||
const TONEMAPPING_SHARED_SHADER_HANDLE: HandleUntyped =
|
||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2499430578245347910);
|
||||
|
||||
const BLIT_SHADER_HANDLE: HandleUntyped =
|
||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2982361071241723543);
|
||||
|
||||
pub struct TonemappingPlugin;
|
||||
|
||||
impl Plugin for TonemappingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
load_internal_asset!(
|
||||
app,
|
||||
TONEMAPPING_SHADER_HANDLE,
|
||||
"tonemapping.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
load_internal_asset!(
|
||||
app,
|
||||
TONEMAPPING_SHARED_SHADER_HANDLE,
|
||||
"tonemapping_shared.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
load_internal_asset!(app, BLIT_SHADER_HANDLE, "blit.wgsl", Shader::from_wgsl);
|
||||
|
||||
app.add_plugin(ExtractComponentPlugin::<Tonemapping>::default());
|
||||
|
||||
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app.init_resource::<TonemappingPipeline>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct TonemappingPipeline {
|
||||
hdr_texture_bind_group: BindGroupLayout,
|
||||
tonemapping_pipeline_id: CachedRenderPipelineId,
|
||||
blit_pipeline_id: CachedRenderPipelineId,
|
||||
}
|
||||
|
||||
impl FromWorld for TonemappingPipeline {
|
||||
fn from_world(render_world: &mut World) -> Self {
|
||||
let tonemap_texture_bind_group = render_world
|
||||
.resource::<RenderDevice>()
|
||||
.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: Some("tonemapping_hdr_texture_bind_group_layout"),
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Texture {
|
||||
sample_type: TextureSampleType::Float { filterable: false },
|
||||
view_dimension: TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let tonemap_descriptor = RenderPipelineDescriptor {
|
||||
label: Some("tonemapping pipeline".into()),
|
||||
layout: Some(vec![tonemap_texture_bind_group.clone()]),
|
||||
vertex: fullscreen_shader_vertex_state(),
|
||||
fragment: Some(FragmentState {
|
||||
shader: TONEMAPPING_SHADER_HANDLE.typed(),
|
||||
shader_defs: vec![],
|
||||
entry_point: "fs_main".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: TextureFormat::bevy_default(),
|
||||
blend: None,
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: MultisampleState::default(),
|
||||
};
|
||||
|
||||
let blit_descriptor = RenderPipelineDescriptor {
|
||||
label: Some("blit pipeline".into()),
|
||||
layout: Some(vec![tonemap_texture_bind_group.clone()]),
|
||||
vertex: fullscreen_shader_vertex_state(),
|
||||
fragment: Some(FragmentState {
|
||||
shader: BLIT_SHADER_HANDLE.typed(),
|
||||
shader_defs: vec![],
|
||||
entry_point: "fs_main".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: TextureFormat::bevy_default(),
|
||||
blend: None,
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: MultisampleState::default(),
|
||||
};
|
||||
let mut cache = render_world.resource_mut::<PipelineCache>();
|
||||
TonemappingPipeline {
|
||||
hdr_texture_bind_group: tonemap_texture_bind_group,
|
||||
tonemapping_pipeline_id: cache.queue_render_pipeline(tonemap_descriptor),
|
||||
blit_pipeline_id: cache.queue_render_pipeline(blit_descriptor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
pub struct Tonemapping {
|
||||
pub is_enabled: bool,
|
||||
}
|
||||
|
||||
impl ExtractComponent for Tonemapping {
|
||||
type Query = &'static Self;
|
||||
type Filter = With<Camera>;
|
||||
|
||||
fn extract_component(item: QueryItem<Self::Query>) -> Self {
|
||||
item.clone()
|
||||
}
|
||||
}
|
133
crates/bevy_core_pipeline/src/tonemapping/node.rs
Normal file
133
crates/bevy_core_pipeline/src/tonemapping/node.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
use std::sync::Mutex;
|
||||
|
||||
use crate::tonemapping::{Tonemapping, TonemappingPipeline};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::query::QueryState;
|
||||
use bevy_render::{
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
||||
render_resource::{
|
||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, LoadOp, Operations,
|
||||
PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor,
|
||||
TextureViewId,
|
||||
},
|
||||
renderer::RenderContext,
|
||||
view::{ExtractedView, ViewMainTexture, ViewTarget},
|
||||
};
|
||||
|
||||
pub struct TonemappingNode {
|
||||
query: QueryState<(&'static ViewTarget, Option<&'static Tonemapping>), With<ExtractedView>>,
|
||||
cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
|
||||
}
|
||||
|
||||
impl TonemappingNode {
|
||||
pub const IN_VIEW: &'static str = "view";
|
||||
|
||||
pub fn new(world: &mut World) -> Self {
|
||||
Self {
|
||||
query: QueryState::new(world),
|
||||
cached_texture_bind_group: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for TonemappingNode {
|
||||
fn input(&self) -> Vec<SlotInfo> {
|
||||
vec![SlotInfo::new(TonemappingNode::IN_VIEW, SlotType::Entity)]
|
||||
}
|
||||
|
||||
fn update(&mut self, world: &mut World) {
|
||||
self.query.update_archetypes(world);
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||
let pipeline_cache = world.resource::<PipelineCache>();
|
||||
let tonemapping_pipeline = world.resource::<TonemappingPipeline>();
|
||||
|
||||
let (target, tonemapping) = match self.query.get_manual(world, view_entity) {
|
||||
Ok(result) => result,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
let ldr_texture = match &target.main_texture {
|
||||
ViewMainTexture::Hdr { ldr_texture, .. } => ldr_texture,
|
||||
ViewMainTexture::Sdr { .. } => {
|
||||
// non-hdr does tone mapping in the main pass node
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let tonemapping_enabled = tonemapping.map_or(false, |t| t.is_enabled);
|
||||
let pipeline_id = if tonemapping_enabled {
|
||||
tonemapping_pipeline.tonemapping_pipeline_id
|
||||
} else {
|
||||
tonemapping_pipeline.blit_pipeline_id
|
||||
};
|
||||
|
||||
let pipeline = match pipeline_cache.get_render_pipeline(pipeline_id) {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let main_texture = target.main_texture.texture();
|
||||
|
||||
let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
|
||||
let bind_group = match &mut *cached_bind_group {
|
||||
Some((id, bind_group)) if main_texture.id() == *id => bind_group,
|
||||
cached_bind_group => {
|
||||
let sampler = render_context
|
||||
.render_device
|
||||
.create_sampler(&SamplerDescriptor::default());
|
||||
|
||||
let bind_group =
|
||||
render_context
|
||||
.render_device
|
||||
.create_bind_group(&BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &tonemapping_pipeline.hdr_texture_bind_group,
|
||||
entries: &[
|
||||
BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: BindingResource::TextureView(main_texture),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: BindingResource::Sampler(&sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let (_, bind_group) = cached_bind_group.insert((main_texture.id(), bind_group));
|
||||
bind_group
|
||||
}
|
||||
};
|
||||
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("tonemapping_pass"),
|
||||
color_attachments: &[Some(RenderPassColorAttachment {
|
||||
view: ldr_texture,
|
||||
resolve_target: None,
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(Default::default()), // TODO shouldn't need to be cleared
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
};
|
||||
|
||||
let mut render_pass = render_context
|
||||
.command_encoder
|
||||
.begin_render_pass(&pass_descriptor);
|
||||
|
||||
render_pass.set_pipeline(pipeline);
|
||||
render_pass.set_bind_group(0, bind_group, &[]);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
14
crates/bevy_core_pipeline/src/tonemapping/tonemapping.wgsl
Normal file
14
crates/bevy_core_pipeline/src/tonemapping/tonemapping.wgsl
Normal file
|
@ -0,0 +1,14 @@
|
|||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#import bevy_core_pipeline::tonemapping
|
||||
|
||||
@group(0) @binding(0)
|
||||
var hdr_texture: texture_2d<f32>;
|
||||
@group(0) @binding(1)
|
||||
var hdr_sampler: sampler;
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
||||
let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv);
|
||||
|
||||
return vec4<f32>(reinhard_luminance(hdr_color.rgb), hdr_color.a);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#define_import_path bevy_core_pipeline::tonemapping
|
||||
|
||||
// from https://64.github.io/tonemapping/
|
||||
// reinhard on RGB oversaturates colors
|
||||
fn tonemapping_reinhard(color: vec3<f32>) -> vec3<f32> {
|
||||
return color / (1.0 + color);
|
||||
}
|
||||
|
||||
fn tonemapping_reinhard_extended(color: vec3<f32>, max_white: f32) -> vec3<f32> {
|
||||
let numerator = color * (1.0 + (color / vec3<f32>(max_white * max_white)));
|
||||
return numerator / (1.0 + color);
|
||||
}
|
||||
|
||||
// luminance coefficients from Rec. 709.
|
||||
// https://en.wikipedia.org/wiki/Rec._709
|
||||
fn tonemapping_luminance(v: vec3<f32>) -> f32 {
|
||||
return dot(v, vec3<f32>(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
fn tonemapping_change_luminance(c_in: vec3<f32>, l_out: f32) -> vec3<f32> {
|
||||
let l_in = tonemapping_luminance(c_in);
|
||||
return c_in * (l_out / l_in);
|
||||
}
|
||||
|
||||
fn reinhard_luminance(color: vec3<f32>) -> vec3<f32> {
|
||||
let l_old = tonemapping_luminance(color);
|
||||
let l_new = l_old / (1.0 + l_old);
|
||||
return tonemapping_change_luminance(color, l_new);
|
||||
}
|
158
crates/bevy_core_pipeline/src/upscaling/mod.rs
Normal file
158
crates/bevy_core_pipeline/src/upscaling/mod.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
mod node;
|
||||
|
||||
pub use node::UpscalingNode;
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{load_internal_asset, HandleUntyped};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::renderer::{RenderDevice, SurfaceTextureFormat};
|
||||
use bevy_render::view::ExtractedView;
|
||||
use bevy_render::{render_resource::*, RenderApp, RenderStage};
|
||||
|
||||
use bevy_reflect::TypeUuid;
|
||||
|
||||
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
|
||||
|
||||
const UPSCALING_SHADER_HANDLE: HandleUntyped =
|
||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 14589267395627146578);
|
||||
|
||||
pub struct UpscalingPlugin;
|
||||
|
||||
impl Plugin for UpscalingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
load_internal_asset!(
|
||||
app,
|
||||
UPSCALING_SHADER_HANDLE,
|
||||
"upscaling.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
|
||||
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app
|
||||
.init_resource::<UpscalingPipeline>()
|
||||
.init_resource::<SpecializedRenderPipelines<UpscalingPipeline>>()
|
||||
.add_system_to_stage(RenderStage::Queue, queue_upscaling_bind_groups);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct UpscalingPipeline {
|
||||
ldr_texture_bind_group: BindGroupLayout,
|
||||
surface_texture_format: TextureFormat,
|
||||
}
|
||||
|
||||
impl FromWorld for UpscalingPipeline {
|
||||
fn from_world(render_world: &mut World) -> Self {
|
||||
let render_device = render_world.resource::<RenderDevice>();
|
||||
let surface_texture_format = render_world.resource::<SurfaceTextureFormat>().0;
|
||||
|
||||
let ldr_texture_bind_group =
|
||||
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: Some("upscaling_ldr_texture_bind_group_layout"),
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Texture {
|
||||
sample_type: TextureSampleType::Float { filterable: false },
|
||||
view_dimension: TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
UpscalingPipeline {
|
||||
ldr_texture_bind_group,
|
||||
surface_texture_format,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum UpscalingMode {
|
||||
Filtering = 0,
|
||||
Nearest = 1,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[repr(transparent)]
|
||||
pub struct UpscalingPipelineKey: u32 {
|
||||
const NONE = 0;
|
||||
const UPSCALING_MODE_RESERVED_BITS = UpscalingPipelineKey::UPSCALING_MODE_MASK_BITS << UpscalingPipelineKey::UPSCALING_MODE_SHIFT_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
impl UpscalingPipelineKey {
|
||||
const UPSCALING_MODE_MASK_BITS: u32 = 0b1111; // enough for 16 different modes
|
||||
const UPSCALING_MODE_SHIFT_BITS: u32 = 32 - 4;
|
||||
|
||||
pub fn from_upscaling_mode(upscaling_mode: UpscalingMode) -> Self {
|
||||
let upscaling_mode_bits = ((upscaling_mode as u32) & Self::UPSCALING_MODE_MASK_BITS)
|
||||
<< Self::UPSCALING_MODE_SHIFT_BITS;
|
||||
UpscalingPipelineKey::from_bits(upscaling_mode_bits).unwrap()
|
||||
}
|
||||
|
||||
pub fn upscaling_mode(&self) -> UpscalingMode {
|
||||
let upscaling_mode_bits =
|
||||
(self.bits >> Self::UPSCALING_MODE_SHIFT_BITS) & Self::UPSCALING_MODE_MASK_BITS;
|
||||
match upscaling_mode_bits {
|
||||
0 => UpscalingMode::Filtering,
|
||||
1 => UpscalingMode::Nearest,
|
||||
other => panic!("invalid upscaling mode bits in UpscalingPipelineKey: {other}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecializedRenderPipeline for UpscalingPipeline {
|
||||
type Key = UpscalingPipelineKey;
|
||||
|
||||
fn specialize(&self, _: Self::Key) -> RenderPipelineDescriptor {
|
||||
RenderPipelineDescriptor {
|
||||
label: Some("upscaling pipeline".into()),
|
||||
layout: Some(vec![self.ldr_texture_bind_group.clone()]),
|
||||
vertex: fullscreen_shader_vertex_state(),
|
||||
fragment: Some(FragmentState {
|
||||
shader: UPSCALING_SHADER_HANDLE.typed(),
|
||||
shader_defs: vec![],
|
||||
entry_point: "fs_main".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: self.surface_texture_format,
|
||||
blend: None,
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: MultisampleState::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct UpscalingTarget {
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
}
|
||||
|
||||
fn queue_upscaling_bind_groups(
|
||||
mut commands: Commands,
|
||||
mut pipeline_cache: ResMut<PipelineCache>,
|
||||
mut pipelines: ResMut<SpecializedRenderPipelines<UpscalingPipeline>>,
|
||||
upscaling_pipeline: Res<UpscalingPipeline>,
|
||||
view_targets: Query<Entity, With<ExtractedView>>,
|
||||
) {
|
||||
for entity in view_targets.iter() {
|
||||
let key = UpscalingPipelineKey::from_upscaling_mode(UpscalingMode::Filtering);
|
||||
let pipeline = pipelines.specialize(&mut pipeline_cache, &upscaling_pipeline, key);
|
||||
|
||||
commands.entity(entity).insert(UpscalingTarget { pipeline });
|
||||
}
|
||||
}
|
123
crates/bevy_core_pipeline/src/upscaling/node.rs
Normal file
123
crates/bevy_core_pipeline/src/upscaling/node.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
use std::sync::Mutex;
|
||||
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::query::QueryState;
|
||||
use bevy_render::{
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
||||
render_resource::{
|
||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, LoadOp, Operations,
|
||||
PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor,
|
||||
TextureViewId,
|
||||
},
|
||||
renderer::RenderContext,
|
||||
view::{ExtractedView, ViewTarget},
|
||||
};
|
||||
|
||||
use super::{UpscalingPipeline, UpscalingTarget};
|
||||
|
||||
pub struct UpscalingNode {
|
||||
query: QueryState<(&'static ViewTarget, &'static UpscalingTarget), With<ExtractedView>>,
|
||||
cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
|
||||
}
|
||||
|
||||
impl UpscalingNode {
|
||||
pub const IN_VIEW: &'static str = "view";
|
||||
|
||||
pub fn new(world: &mut World) -> Self {
|
||||
Self {
|
||||
query: QueryState::new(world),
|
||||
cached_texture_bind_group: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for UpscalingNode {
|
||||
fn input(&self) -> Vec<SlotInfo> {
|
||||
vec![SlotInfo::new(UpscalingNode::IN_VIEW, SlotType::Entity)]
|
||||
}
|
||||
|
||||
fn update(&mut self, world: &mut World) {
|
||||
self.query.update_archetypes(world);
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||
|
||||
let pipeline_cache = world.get_resource::<PipelineCache>().unwrap();
|
||||
let upscaling_pipeline = world.get_resource::<UpscalingPipeline>().unwrap();
|
||||
|
||||
let (target, upscaling_target) = match self.query.get_manual(world, view_entity) {
|
||||
Ok(query) => query,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
let upscaled_texture = match &target.main_texture {
|
||||
bevy_render::view::ViewMainTexture::Hdr { ldr_texture, .. } => ldr_texture,
|
||||
bevy_render::view::ViewMainTexture::Sdr { texture, .. } => texture,
|
||||
};
|
||||
|
||||
let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
|
||||
let bind_group = match &mut *cached_bind_group {
|
||||
Some((id, bind_group)) if upscaled_texture.id() == *id => bind_group,
|
||||
cached_bind_group => {
|
||||
let sampler = render_context
|
||||
.render_device
|
||||
.create_sampler(&SamplerDescriptor::default());
|
||||
|
||||
let bind_group =
|
||||
render_context
|
||||
.render_device
|
||||
.create_bind_group(&BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &upscaling_pipeline.ldr_texture_bind_group,
|
||||
entries: &[
|
||||
BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: BindingResource::TextureView(upscaled_texture),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: BindingResource::Sampler(&sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let (_, bind_group) = cached_bind_group.insert((upscaled_texture.id(), bind_group));
|
||||
bind_group
|
||||
}
|
||||
};
|
||||
|
||||
let pipeline = match pipeline_cache.get_render_pipeline(upscaling_target.pipeline) {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("upscaling_pass"),
|
||||
color_attachments: &[Some(RenderPassColorAttachment {
|
||||
view: &target.out_texture,
|
||||
resolve_target: None,
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(Default::default()), // TODO dont_care
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
};
|
||||
|
||||
let mut render_pass = render_context
|
||||
.command_encoder
|
||||
.begin_render_pass(&pass_descriptor);
|
||||
|
||||
render_pass.set_pipeline(pipeline);
|
||||
render_pass.set_bind_group(0, bind_group, &[]);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
13
crates/bevy_core_pipeline/src/upscaling/upscaling.wgsl
Normal file
13
crates/bevy_core_pipeline/src/upscaling/upscaling.wgsl
Normal file
|
@ -0,0 +1,13 @@
|
|||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
|
||||
@group(0) @binding(0)
|
||||
var hdr_texture: texture_2d<f32>;
|
||||
@group(0) @binding(1)
|
||||
var hdr_sampler: sampler;
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
||||
let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv);
|
||||
|
||||
return hdr_color;
|
||||
}
|
|
@ -4,7 +4,10 @@ use crate::{
|
|||
};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
|
||||
use bevy_core_pipeline::core_3d::{AlphaMask3d, Opaque3d, Transparent3d};
|
||||
use bevy_core_pipeline::{
|
||||
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
|
||||
tonemapping::Tonemapping,
|
||||
};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
|
@ -327,6 +330,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&VisibleEntities,
|
||||
Option<&Tonemapping>,
|
||||
&mut RenderPhase<Opaque3d>,
|
||||
&mut RenderPhase<AlphaMask3d>,
|
||||
&mut RenderPhase<Transparent3d>,
|
||||
|
@ -334,8 +338,14 @@ pub fn queue_material_meshes<M: Material>(
|
|||
) where
|
||||
M::Data: PartialEq + Eq + Hash + Clone,
|
||||
{
|
||||
for (view, visible_entities, mut opaque_phase, mut alpha_mask_phase, mut transparent_phase) in
|
||||
&mut views
|
||||
for (
|
||||
view,
|
||||
visible_entities,
|
||||
tonemapping,
|
||||
mut opaque_phase,
|
||||
mut alpha_mask_phase,
|
||||
mut transparent_phase,
|
||||
) in &mut views
|
||||
{
|
||||
let draw_opaque_pbr = opaque_draw_functions
|
||||
.read()
|
||||
|
@ -350,8 +360,15 @@ pub fn queue_material_meshes<M: Material>(
|
|||
.get_id::<DrawMaterial<M>>()
|
||||
.unwrap();
|
||||
|
||||
let mut view_key =
|
||||
MeshPipelineKey::from_msaa_samples(msaa.samples) | MeshPipelineKey::from_hdr(view.hdr);
|
||||
|
||||
if let Some(tonemapping) = tonemapping {
|
||||
if tonemapping.is_enabled && !view.hdr {
|
||||
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
|
||||
}
|
||||
}
|
||||
let rangefinder = view.rangefinder3d();
|
||||
let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples);
|
||||
|
||||
for visible_entity in &visible_entities.entities {
|
||||
if let Ok((material_handle, mesh_handle, mesh_uniform)) =
|
||||
|
@ -361,7 +378,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||
if let Some(mesh) = render_meshes.get(mesh_handle) {
|
||||
let mut mesh_key =
|
||||
MeshPipelineKey::from_primitive_topology(mesh.primitive_topology)
|
||||
| msaa_key;
|
||||
| view_key;
|
||||
let alpha_mode = material.properties.alpha_mode;
|
||||
if let AlphaMode::Blend = alpha_mode {
|
||||
mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS;
|
||||
|
|
|
@ -1031,6 +1031,7 @@ pub fn prepare_lights(
|
|||
),
|
||||
transform: view_translation * *view_rotation,
|
||||
projection: cube_face_projection,
|
||||
hdr: false,
|
||||
},
|
||||
RenderPhase::<Shadow>::default(),
|
||||
LightEntity::Point {
|
||||
|
@ -1086,6 +1087,7 @@ pub fn prepare_lights(
|
|||
),
|
||||
transform: spot_view_transform,
|
||||
projection: spot_projection,
|
||||
hdr: false,
|
||||
},
|
||||
RenderPhase::<Shadow>::default(),
|
||||
LightEntity::Spot { light_entity },
|
||||
|
@ -1169,6 +1171,7 @@ pub fn prepare_lights(
|
|||
),
|
||||
transform: GlobalTransform::from(view.inverse()),
|
||||
projection,
|
||||
hdr: false,
|
||||
},
|
||||
RenderPhase::<Shadow>::default(),
|
||||
LightEntity::Directional { light_entity },
|
||||
|
|
|
@ -21,9 +21,11 @@ use bevy_render::{
|
|||
render_asset::RenderAssets,
|
||||
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue, RenderTextureFormat},
|
||||
texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo},
|
||||
view::{ComputedVisibility, ViewUniform, ViewUniformOffset, ViewUniforms},
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{
|
||||
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
|
||||
},
|
||||
view::{ComputedVisibility, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms},
|
||||
Extract, RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
|
@ -268,10 +270,8 @@ impl FromWorld for MeshPipeline {
|
|||
Res<RenderDevice>,
|
||||
Res<DefaultImageSampler>,
|
||||
Res<RenderQueue>,
|
||||
Res<RenderTextureFormat>,
|
||||
)> = SystemState::new(world);
|
||||
let (render_device, default_sampler, render_queue, first_available_texture_format) =
|
||||
system_state.get_mut(world);
|
||||
let (render_device, default_sampler, render_queue) = system_state.get_mut(world);
|
||||
let clustered_forward_buffer_binding_type = render_device
|
||||
.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
|
||||
|
||||
|
@ -438,7 +438,7 @@ impl FromWorld for MeshPipeline {
|
|||
Extent3d::default(),
|
||||
TextureDimension::D2,
|
||||
&[255u8; 4],
|
||||
first_available_texture_format.0,
|
||||
TextureFormat::bevy_default(),
|
||||
);
|
||||
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||
let sampler = match image.sampler_descriptor {
|
||||
|
@ -516,6 +516,8 @@ bitflags::bitflags! {
|
|||
pub struct MeshPipelineKey: u32 {
|
||||
const NONE = 0;
|
||||
const TRANSPARENT_MAIN_PASS = (1 << 0);
|
||||
const HDR = (1 << 1);
|
||||
const TONEMAP_IN_SHADER = (1 << 2);
|
||||
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
|
||||
const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
|
||||
}
|
||||
|
@ -533,6 +535,14 @@ impl MeshPipelineKey {
|
|||
Self::from_bits(msaa_bits).unwrap()
|
||||
}
|
||||
|
||||
pub fn from_hdr(hdr: bool) -> Self {
|
||||
if hdr {
|
||||
MeshPipelineKey::HDR
|
||||
} else {
|
||||
MeshPipelineKey::NONE
|
||||
}
|
||||
}
|
||||
|
||||
pub fn msaa_samples(&self) -> u32 {
|
||||
1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
|
||||
}
|
||||
|
@ -624,6 +634,15 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||
depth_write_enabled = true;
|
||||
}
|
||||
|
||||
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
|
||||
shader_defs.push("TONEMAP_IN_SHADER".to_string());
|
||||
}
|
||||
|
||||
let format = match key.contains(MeshPipelineKey::HDR) {
|
||||
true => ViewTarget::TEXTURE_FORMAT_HDR,
|
||||
false => TextureFormat::bevy_default(),
|
||||
};
|
||||
|
||||
Ok(RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
shader: MESH_SHADER_HANDLE.typed::<Shader>(),
|
||||
|
@ -636,7 +655,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||
shader_defs,
|
||||
entry_point: "fragment".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: self.dummy_white_gpu_image.texture_format,
|
||||
format,
|
||||
blend,
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
|
|
|
@ -87,8 +87,10 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||
in.is_front,
|
||||
);
|
||||
pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic);
|
||||
|
||||
output_color = tone_mapping(pbr(pbr_input));
|
||||
output_color = pbr(pbr_input);
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
output_color = tone_mapping(output_color);
|
||||
#endif
|
||||
} else {
|
||||
output_color = alpha_discard(material, output_color);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#define_import_path bevy_pbr::pbr_functions
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
#import bevy_core_pipeline::tonemapping
|
||||
#endif
|
||||
|
||||
|
||||
fn alpha_discard(material: StandardMaterial, output_color: vec4<f32>) -> vec4<f32>{
|
||||
var color = output_color;
|
||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u) {
|
||||
|
@ -245,6 +250,7 @@ fn pbr(
|
|||
return output_color;
|
||||
}
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
||||
// tone_mapping
|
||||
return vec4<f32>(reinhard_luminance(in.rgb), in.a);
|
||||
|
@ -253,4 +259,5 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
|||
// Not needed with sRGB buffer
|
||||
// output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -149,41 +149,6 @@ fn perceptualRoughnessToRoughness(perceptualRoughness: f32) -> f32 {
|
|||
return clampedPerceptualRoughness * clampedPerceptualRoughness;
|
||||
}
|
||||
|
||||
// from https://64.github.io/tonemapping/
|
||||
// reinhard on RGB oversaturates colors
|
||||
fn reinhard(color: vec3<f32>) -> vec3<f32> {
|
||||
return color / (1.0 + color);
|
||||
}
|
||||
|
||||
fn reinhard_extended(color: vec3<f32>, max_white: f32) -> vec3<f32> {
|
||||
let numerator = color * (1.0 + (color / vec3<f32>(max_white * max_white)));
|
||||
return numerator / (1.0 + color);
|
||||
}
|
||||
|
||||
// luminance coefficients from Rec. 709.
|
||||
// https://en.wikipedia.org/wiki/Rec._709
|
||||
fn luminance(v: vec3<f32>) -> f32 {
|
||||
return dot(v, vec3<f32>(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
fn change_luminance(c_in: vec3<f32>, l_out: f32) -> vec3<f32> {
|
||||
let l_in = luminance(c_in);
|
||||
return c_in * (l_out / l_in);
|
||||
}
|
||||
|
||||
fn reinhard_luminance(color: vec3<f32>) -> vec3<f32> {
|
||||
let l_old = luminance(color);
|
||||
let l_new = l_old / (1.0 + l_old);
|
||||
return change_luminance(color, l_new);
|
||||
}
|
||||
|
||||
fn reinhard_extended_luminance(color: vec3<f32>, max_white_l: f32) -> vec3<f32> {
|
||||
let l_old = luminance(color);
|
||||
let numerator = l_old * (1.0 + (l_old / (max_white_l * max_white_l)));
|
||||
let l_new = numerator / (1.0 + l_old);
|
||||
return change_luminance(color, l_new);
|
||||
}
|
||||
|
||||
fn point_light(
|
||||
world_position: vec3<f32>, light: PointLight, roughness: f32, NdotV: f32, N: vec3<f32>, V: vec3<f32>,
|
||||
R: vec3<f32>, F0: vec3<f32>, diffuseColor: vec3<f32>
|
||||
|
|
|
@ -124,10 +124,11 @@ fn queue_wireframes(
|
|||
for (view, visible_entities, mut opaque_phase) in &mut views {
|
||||
let rangefinder = view.rangefinder3d();
|
||||
|
||||
let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr);
|
||||
let add_render_phase =
|
||||
|(entity, mesh_handle, mesh_uniform): (Entity, &Handle<Mesh>, &MeshUniform)| {
|
||||
if let Some(mesh) = render_meshes.get(mesh_handle) {
|
||||
let key = msaa_key
|
||||
let key = view_key
|
||||
| MeshPipelineKey::from_primitive_topology(mesh.primitive_topology);
|
||||
let pipeline_id = pipelines.specialize(
|
||||
&mut pipeline_cache,
|
||||
|
|
|
@ -96,6 +96,12 @@ pub struct Camera {
|
|||
/// The "target" that this camera will render to.
|
||||
#[reflect(ignore)]
|
||||
pub target: RenderTarget,
|
||||
/// If this is set to `true`, the camera will use an intermediate "high dynamic range" render texture.
|
||||
/// Warning: we are still working on this feature. If MSAA is enabled, there will be artifacts in
|
||||
/// some cases. When rendering with WebGL, this will crash if MSAA is enabled.
|
||||
/// See <https://github.com/bevyengine/bevy/pull/3425> for details.
|
||||
// TODO: resolve the issues mentioned in the doc comment above, then remove the warning.
|
||||
pub hdr: bool,
|
||||
}
|
||||
|
||||
impl Default for Camera {
|
||||
|
@ -106,6 +112,7 @@ impl Default for Camera {
|
|||
viewport: None,
|
||||
computed: Default::default(),
|
||||
target: Default::default(),
|
||||
hdr: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -478,6 +485,7 @@ pub fn extract_cameras(
|
|||
ExtractedView {
|
||||
projection: camera.projection_matrix(),
|
||||
transform: *transform,
|
||||
hdr: camera.hdr,
|
||||
viewport: UVec4::new(
|
||||
viewport_origin.x,
|
||||
viewport_origin.y,
|
||||
|
|
|
@ -48,7 +48,7 @@ use crate::{
|
|||
primitives::{CubemapFrusta, Frustum},
|
||||
render_graph::RenderGraph,
|
||||
render_resource::{PipelineCache, Shader, ShaderLoader},
|
||||
renderer::{render_system, RenderInstance, RenderTextureFormat},
|
||||
renderer::{render_system, RenderInstance, SurfaceTextureFormat},
|
||||
texture::BevyDefault,
|
||||
view::{ViewPlugin, WindowRenderPlugin},
|
||||
};
|
||||
|
@ -166,7 +166,7 @@ impl Plugin for RenderPlugin {
|
|||
&options,
|
||||
&request_adapter_options,
|
||||
));
|
||||
let texture_format = RenderTextureFormat(
|
||||
let texture_format = SurfaceTextureFormat(
|
||||
available_texture_formats
|
||||
.get(0)
|
||||
.cloned()
|
||||
|
|
|
@ -102,10 +102,10 @@ pub struct RenderInstance(pub Instance);
|
|||
#[derive(Resource, Clone, Deref, DerefMut)]
|
||||
pub struct RenderAdapterInfo(pub AdapterInfo);
|
||||
|
||||
/// The [`TextureFormat`](wgpu::TextureFormat) used for rendering.
|
||||
/// The [`TextureFormat`](wgpu::TextureFormat) used for rendering to window surfaces.
|
||||
/// Initially it's the first element in `AvailableTextureFormats`, or Bevy default format.
|
||||
#[derive(Resource, Clone, Deref, DerefMut)]
|
||||
pub struct RenderTextureFormat(pub wgpu::TextureFormat);
|
||||
pub struct SurfaceTextureFormat(pub wgpu::TextureFormat);
|
||||
|
||||
/// The available [`TextureFormat`](wgpu::TextureFormat)s on the [`RenderAdapter`].
|
||||
/// Will be inserted as a `Resource` after the renderer is initialized.
|
||||
|
|
|
@ -111,11 +111,6 @@ pub trait BevyDefault {
|
|||
|
||||
impl BevyDefault for wgpu::TextureFormat {
|
||||
fn bevy_default() -> Self {
|
||||
if cfg!(target_os = "android") || cfg!(target_arch = "wasm32") {
|
||||
// Bgra8UnormSrgb texture missing on some Android devices
|
||||
wgpu::TextureFormat::Rgba8UnormSrgb
|
||||
} else {
|
||||
wgpu::TextureFormat::Bgra8UnormSrgb
|
||||
}
|
||||
wgpu::TextureFormat::Rgba8UnormSrgb
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
pub mod visibility;
|
||||
pub mod window;
|
||||
|
||||
use bevy_utils::HashMap;
|
||||
pub use visibility::*;
|
||||
use wgpu::{
|
||||
Color, Extent3d, Operations, RenderPassColorAttachment, TextureDescriptor, TextureDimension,
|
||||
TextureUsages,
|
||||
TextureFormat, TextureUsages,
|
||||
};
|
||||
pub use window::*;
|
||||
|
||||
|
@ -15,8 +16,8 @@ use crate::{
|
|||
rangefinder::ViewRangefinder3d,
|
||||
render_asset::RenderAssets,
|
||||
render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView},
|
||||
renderer::{RenderDevice, RenderQueue, RenderTextureFormat},
|
||||
texture::TextureCache,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, TextureCache},
|
||||
RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_app::{App, Plugin};
|
||||
|
@ -24,7 +25,6 @@ use bevy_ecs::prelude::*;
|
|||
use bevy_math::{Mat4, UVec4, Vec3, Vec4};
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use bevy_utils::HashMap;
|
||||
|
||||
pub struct ViewPlugin;
|
||||
|
||||
|
@ -81,6 +81,7 @@ impl Default for Msaa {
|
|||
pub struct ExtractedView {
|
||||
pub projection: Mat4,
|
||||
pub transform: GlobalTransform,
|
||||
pub hdr: bool,
|
||||
// uvec4(origin.x, origin.y, width, height)
|
||||
pub viewport: UVec4,
|
||||
}
|
||||
|
@ -115,21 +116,74 @@ pub struct ViewUniformOffset {
|
|||
pub offset: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ViewMainTexture {
|
||||
Hdr {
|
||||
hdr_texture: TextureView,
|
||||
sampled_hdr_texture: Option<TextureView>,
|
||||
|
||||
ldr_texture: TextureView,
|
||||
},
|
||||
Sdr {
|
||||
texture: TextureView,
|
||||
sampled_texture: Option<TextureView>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ViewMainTexture {
|
||||
pub fn texture(&self) -> &TextureView {
|
||||
match self {
|
||||
ViewMainTexture::Hdr { hdr_texture, .. } => hdr_texture,
|
||||
ViewMainTexture::Sdr { texture, .. } => texture,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct ViewTarget {
|
||||
pub view: TextureView,
|
||||
pub sampled_target: Option<TextureView>,
|
||||
pub main_texture: ViewMainTexture,
|
||||
pub out_texture: TextureView,
|
||||
}
|
||||
|
||||
impl ViewTarget {
|
||||
pub const TEXTURE_FORMAT_HDR: TextureFormat = TextureFormat::Rgba16Float;
|
||||
|
||||
pub fn get_color_attachment(&self, ops: Operations<Color>) -> RenderPassColorAttachment {
|
||||
RenderPassColorAttachment {
|
||||
view: self.sampled_target.as_ref().unwrap_or(&self.view),
|
||||
resolve_target: if self.sampled_target.is_some() {
|
||||
Some(&self.view)
|
||||
} else {
|
||||
None
|
||||
let (target, sampled) = match &self.main_texture {
|
||||
ViewMainTexture::Hdr {
|
||||
hdr_texture,
|
||||
sampled_hdr_texture,
|
||||
..
|
||||
} => (hdr_texture, sampled_hdr_texture),
|
||||
ViewMainTexture::Sdr {
|
||||
texture,
|
||||
sampled_texture,
|
||||
} => (texture, sampled_texture),
|
||||
};
|
||||
match sampled {
|
||||
Some(sampled_target) => RenderPassColorAttachment {
|
||||
view: sampled_target,
|
||||
resolve_target: Some(target),
|
||||
ops,
|
||||
},
|
||||
None => RenderPassColorAttachment {
|
||||
view: target,
|
||||
resolve_target: None,
|
||||
ops,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unsampled_color_attachment(
|
||||
&self,
|
||||
ops: Operations<Color>,
|
||||
) -> RenderPassColorAttachment {
|
||||
RenderPassColorAttachment {
|
||||
view: match &self.main_texture {
|
||||
ViewMainTexture::Hdr { hdr_texture, .. } => hdr_texture,
|
||||
ViewMainTexture::Sdr { texture, .. } => texture,
|
||||
},
|
||||
resolve_target: None,
|
||||
ops,
|
||||
}
|
||||
}
|
||||
|
@ -182,42 +236,89 @@ fn prepare_view_targets(
|
|||
images: Res<RenderAssets<Image>>,
|
||||
msaa: Res<Msaa>,
|
||||
render_device: Res<RenderDevice>,
|
||||
texture_format: Res<RenderTextureFormat>,
|
||||
mut texture_cache: ResMut<TextureCache>,
|
||||
cameras: Query<(Entity, &ExtractedCamera)>,
|
||||
cameras: Query<(Entity, &ExtractedCamera, &ExtractedView)>,
|
||||
) {
|
||||
let mut sampled_textures = HashMap::default();
|
||||
for (entity, camera) in &cameras {
|
||||
let mut textures = HashMap::default();
|
||||
for (entity, camera, view) in cameras.iter() {
|
||||
if let Some(target_size) = camera.physical_target_size {
|
||||
if let Some(texture_view) = camera.target.get_texture_view(&windows, &images) {
|
||||
let sampled_target = if msaa.samples > 1 {
|
||||
let sampled_texture = sampled_textures
|
||||
.entry(camera.target.clone())
|
||||
.or_insert_with(|| {
|
||||
texture_cache.get(
|
||||
let size = Extent3d {
|
||||
width: target_size.x,
|
||||
height: target_size.y,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
let main_texture = textures
|
||||
.entry((camera.target.clone(), view.hdr))
|
||||
.or_insert_with(|| {
|
||||
let main_texture_format = if view.hdr {
|
||||
ViewTarget::TEXTURE_FORMAT_HDR
|
||||
} else {
|
||||
TextureFormat::bevy_default()
|
||||
};
|
||||
|
||||
let main_texture = texture_cache.get(
|
||||
&render_device,
|
||||
TextureDescriptor {
|
||||
label: Some("main_texture"),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: main_texture_format,
|
||||
usage: TextureUsages::RENDER_ATTACHMENT
|
||||
| TextureUsages::TEXTURE_BINDING,
|
||||
},
|
||||
);
|
||||
|
||||
let sampled_main_texture = (msaa.samples > 1).then(|| {
|
||||
texture_cache
|
||||
.get(
|
||||
&render_device,
|
||||
TextureDescriptor {
|
||||
label: Some("main_texture_sampled"),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: msaa.samples,
|
||||
dimension: TextureDimension::D2,
|
||||
format: main_texture_format,
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
},
|
||||
)
|
||||
.default_view
|
||||
});
|
||||
if view.hdr {
|
||||
let ldr_texture = texture_cache.get(
|
||||
&render_device,
|
||||
TextureDescriptor {
|
||||
label: Some("sampled_color_attachment_texture"),
|
||||
size: Extent3d {
|
||||
width: target_size.x,
|
||||
height: target_size.y,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
label: Some("ldr_texture"),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: msaa.samples,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: **texture_format,
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
format: TextureFormat::bevy_default(),
|
||||
usage: TextureUsages::RENDER_ATTACHMENT
|
||||
| TextureUsages::TEXTURE_BINDING,
|
||||
},
|
||||
)
|
||||
});
|
||||
Some(sampled_texture.default_view.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
);
|
||||
|
||||
ViewMainTexture::Hdr {
|
||||
hdr_texture: main_texture.default_view,
|
||||
sampled_hdr_texture: sampled_main_texture,
|
||||
ldr_texture: ldr_texture.default_view,
|
||||
}
|
||||
} else {
|
||||
ViewMainTexture::Sdr {
|
||||
texture: main_texture.default_view,
|
||||
sampled_texture: sampled_main_texture,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
commands.entity(entity).insert(ViewTarget {
|
||||
view: texture_view.clone(),
|
||||
sampled_target,
|
||||
main_texture: main_texture.clone(),
|
||||
out_texture: texture_view.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
|
||||
use bevy_core_pipeline::core_2d::Transparent2d;
|
||||
use bevy_core_pipeline::{core_2d::Transparent2d, tonemapping::Tonemapping};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
|
@ -31,7 +31,7 @@ use bevy_render::{
|
|||
},
|
||||
renderer::RenderDevice,
|
||||
texture::FallbackImage,
|
||||
view::{ComputedVisibility, Msaa, Visibility, VisibleEntities},
|
||||
view::{ComputedVisibility, ExtractedView, Msaa, Visibility, VisibleEntities},
|
||||
Extract, RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_transform::components::{GlobalTransform, Transform};
|
||||
|
@ -306,20 +306,33 @@ pub fn queue_material2d_meshes<M: Material2d>(
|
|||
render_meshes: Res<RenderAssets<Mesh>>,
|
||||
render_materials: Res<RenderMaterials2d<M>>,
|
||||
material2d_meshes: Query<(&Handle<M>, &Mesh2dHandle, &Mesh2dUniform)>,
|
||||
mut views: Query<(&VisibleEntities, &mut RenderPhase<Transparent2d>)>,
|
||||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&VisibleEntities,
|
||||
Option<&Tonemapping>,
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
)>,
|
||||
) where
|
||||
M::Data: PartialEq + Eq + Hash + Clone,
|
||||
{
|
||||
if material2d_meshes.is_empty() {
|
||||
return;
|
||||
}
|
||||
for (visible_entities, mut transparent_phase) in &mut views {
|
||||
|
||||
for (view, visible_entities, tonemapping, mut transparent_phase) in &mut views {
|
||||
let draw_transparent_pbr = transparent_draw_functions
|
||||
.read()
|
||||
.get_id::<DrawMaterial2d<M>>()
|
||||
.unwrap();
|
||||
|
||||
let msaa_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples);
|
||||
let mut view_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples)
|
||||
| Mesh2dPipelineKey::from_hdr(view.hdr);
|
||||
|
||||
if let Some(tonemapping) = tonemapping {
|
||||
if tonemapping.is_enabled && !view.hdr {
|
||||
view_key |= Mesh2dPipelineKey::TONEMAP_IN_SHADER;
|
||||
}
|
||||
}
|
||||
|
||||
for visible_entity in &visible_entities.entities {
|
||||
if let Ok((material2d_handle, mesh2d_handle, mesh2d_uniform)) =
|
||||
|
@ -327,7 +340,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
|
|||
{
|
||||
if let Some(material2d) = render_materials.get(material2d_handle) {
|
||||
if let Some(mesh) = render_meshes.get(&mesh2d_handle.0) {
|
||||
let mesh_key = msaa_key
|
||||
let mesh_key = view_key
|
||||
| Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology);
|
||||
|
||||
let pipeline_id = pipelines.specialize(
|
||||
|
|
|
@ -13,9 +13,13 @@ use bevy_render::{
|
|||
render_asset::RenderAssets,
|
||||
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue, RenderTextureFormat},
|
||||
texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo},
|
||||
view::{ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms},
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{
|
||||
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
|
||||
},
|
||||
view::{
|
||||
ComputedVisibility, ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms,
|
||||
},
|
||||
Extract, RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
|
@ -157,13 +161,9 @@ pub struct Mesh2dPipeline {
|
|||
|
||||
impl FromWorld for Mesh2dPipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let mut system_state: SystemState<(
|
||||
Res<RenderDevice>,
|
||||
Res<DefaultImageSampler>,
|
||||
Res<RenderTextureFormat>,
|
||||
)> = SystemState::new(world);
|
||||
let (render_device, default_sampler, first_available_texture_format) =
|
||||
system_state.get_mut(world);
|
||||
let mut system_state: SystemState<(Res<RenderDevice>, Res<DefaultImageSampler>)> =
|
||||
SystemState::new(world);
|
||||
let (render_device, default_sampler) = system_state.get_mut(world);
|
||||
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
// View
|
||||
|
@ -210,7 +210,7 @@ impl FromWorld for Mesh2dPipeline {
|
|||
Extent3d::default(),
|
||||
TextureDimension::D2,
|
||||
&[255u8; 4],
|
||||
first_available_texture_format.0,
|
||||
TextureFormat::bevy_default(),
|
||||
);
|
||||
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||
let sampler = match image.sampler_descriptor {
|
||||
|
@ -286,6 +286,8 @@ bitflags::bitflags! {
|
|||
// FIXME: make normals optional?
|
||||
pub struct Mesh2dPipelineKey: u32 {
|
||||
const NONE = 0;
|
||||
const HDR = (1 << 0);
|
||||
const TONEMAP_IN_SHADER = (1 << 1);
|
||||
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
|
||||
const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
|
||||
}
|
||||
|
@ -303,6 +305,14 @@ impl Mesh2dPipelineKey {
|
|||
Self::from_bits(msaa_bits).unwrap()
|
||||
}
|
||||
|
||||
pub fn from_hdr(hdr: bool) -> Self {
|
||||
if hdr {
|
||||
Mesh2dPipelineKey::HDR
|
||||
} else {
|
||||
Mesh2dPipelineKey::NONE
|
||||
}
|
||||
}
|
||||
|
||||
pub fn msaa_samples(&self) -> u32 {
|
||||
1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
|
||||
}
|
||||
|
@ -364,8 +374,17 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
|
|||
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4));
|
||||
}
|
||||
|
||||
if key.contains(Mesh2dPipelineKey::TONEMAP_IN_SHADER) {
|
||||
shader_defs.push("TONEMAP_IN_SHADER".to_string());
|
||||
}
|
||||
|
||||
let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;
|
||||
|
||||
let format = match key.contains(Mesh2dPipelineKey::HDR) {
|
||||
true => ViewTarget::TEXTURE_FORMAT_HDR,
|
||||
false => TextureFormat::bevy_default(),
|
||||
};
|
||||
|
||||
Ok(RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
shader: MESH2D_SHADER_HANDLE.typed::<Shader>(),
|
||||
|
@ -378,7 +397,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
|
|||
shader_defs,
|
||||
entry_point: "fragment".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: self.dummy_white_gpu_image.texture_format,
|
||||
format,
|
||||
blend: Some(BlendState::ALPHA_BLENDING),
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
Sprite, SPRITE_SHADER_HANDLE,
|
||||
};
|
||||
use bevy_asset::{AssetEvent, Assets, Handle, HandleId};
|
||||
use bevy_core_pipeline::core_2d::Transparent2d;
|
||||
use bevy_core_pipeline::{core_2d::Transparent2d, tonemapping::Tonemapping};
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
system::{lifetimeless::*, SystemParamItem, SystemState},
|
||||
|
@ -20,10 +20,13 @@ use bevy_render::{
|
|||
RenderPhase, SetItemPipeline, TrackedRenderPass,
|
||||
},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue, RenderTextureFormat},
|
||||
texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo},
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{
|
||||
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
|
||||
},
|
||||
view::{
|
||||
ComputedVisibility, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities,
|
||||
ComputedVisibility, ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset,
|
||||
ViewUniforms, VisibleEntities,
|
||||
},
|
||||
Extract,
|
||||
};
|
||||
|
@ -46,10 +49,8 @@ impl FromWorld for SpritePipeline {
|
|||
Res<RenderDevice>,
|
||||
Res<DefaultImageSampler>,
|
||||
Res<RenderQueue>,
|
||||
Res<RenderTextureFormat>,
|
||||
)> = SystemState::new(world);
|
||||
let (render_device, default_sampler, render_queue, first_available_texture_format) =
|
||||
system_state.get_mut(world);
|
||||
let (render_device, default_sampler, render_queue) = system_state.get_mut(world);
|
||||
|
||||
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
|
@ -91,7 +92,7 @@ impl FromWorld for SpritePipeline {
|
|||
Extent3d::default(),
|
||||
TextureDimension::D2,
|
||||
&[255u8; 4],
|
||||
first_available_texture_format.0,
|
||||
TextureFormat::bevy_default(),
|
||||
);
|
||||
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||
let sampler = match image.sampler_descriptor {
|
||||
|
@ -148,6 +149,8 @@ bitflags::bitflags! {
|
|||
pub struct SpritePipelineKey: u32 {
|
||||
const NONE = 0;
|
||||
const COLORED = (1 << 0);
|
||||
const HDR = (1 << 1);
|
||||
const TONEMAP_IN_SHADER = (1 << 2);
|
||||
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +168,22 @@ impl SpritePipelineKey {
|
|||
pub fn msaa_samples(&self) -> u32 {
|
||||
1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
|
||||
}
|
||||
|
||||
pub fn from_colored(colored: bool) -> Self {
|
||||
if colored {
|
||||
SpritePipelineKey::COLORED
|
||||
} else {
|
||||
SpritePipelineKey::NONE
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_hdr(hdr: bool) -> Self {
|
||||
if hdr {
|
||||
SpritePipelineKey::HDR
|
||||
} else {
|
||||
SpritePipelineKey::NONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecializedRenderPipeline for SpritePipeline {
|
||||
|
@ -191,6 +210,15 @@ impl SpecializedRenderPipeline for SpritePipeline {
|
|||
shader_defs.push("COLORED".to_string());
|
||||
}
|
||||
|
||||
if key.contains(SpritePipelineKey::TONEMAP_IN_SHADER) {
|
||||
shader_defs.push("TONEMAP_IN_SHADER".to_string());
|
||||
}
|
||||
|
||||
let format = match key.contains(SpritePipelineKey::HDR) {
|
||||
true => ViewTarget::TEXTURE_FORMAT_HDR,
|
||||
false => TextureFormat::bevy_default(),
|
||||
};
|
||||
|
||||
RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
shader: SPRITE_SHADER_HANDLE.typed::<Shader>(),
|
||||
|
@ -203,7 +231,7 @@ impl SpecializedRenderPipeline for SpritePipeline {
|
|||
shader_defs,
|
||||
entry_point: "fragment".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: self.dummy_white_gpu_image.texture_format,
|
||||
format,
|
||||
blend: Some(BlendState::ALPHA_BLENDING),
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
|
@ -418,7 +446,12 @@ pub fn queue_sprites(
|
|||
gpu_images: Res<RenderAssets<Image>>,
|
||||
msaa: Res<Msaa>,
|
||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
||||
mut views: Query<(&VisibleEntities, &mut RenderPhase<Transparent2d>)>,
|
||||
mut views: Query<(
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
&VisibleEntities,
|
||||
&ExtractedView,
|
||||
Option<&Tonemapping>,
|
||||
)>,
|
||||
events: Res<SpriteAssetEvents>,
|
||||
) {
|
||||
// If an image has changed, the GpuImage has (probably) changed
|
||||
|
@ -431,6 +464,8 @@ pub fn queue_sprites(
|
|||
};
|
||||
}
|
||||
|
||||
let msaa_key = SpritePipelineKey::from_msaa_samples(msaa.samples);
|
||||
|
||||
if let Some(view_binding) = view_uniforms.uniforms.binding() {
|
||||
let sprite_meta = &mut sprite_meta;
|
||||
|
||||
|
@ -448,18 +483,13 @@ pub fn queue_sprites(
|
|||
}));
|
||||
|
||||
let draw_sprite_function = draw_functions.read().get_id::<DrawSprite>().unwrap();
|
||||
let key = SpritePipelineKey::from_msaa_samples(msaa.samples);
|
||||
let pipeline = pipelines.specialize(&mut pipeline_cache, &sprite_pipeline, key);
|
||||
let colored_pipeline = pipelines.specialize(
|
||||
&mut pipeline_cache,
|
||||
&sprite_pipeline,
|
||||
key | SpritePipelineKey::COLORED,
|
||||
);
|
||||
|
||||
// Vertex buffer indices
|
||||
let mut index = 0;
|
||||
let mut colored_index = 0;
|
||||
|
||||
// FIXME: VisibleEntities is ignored
|
||||
|
||||
let extracted_sprites = &mut extracted_sprites.sprites;
|
||||
// Sort sprites by z for correct transparency and then by handle to improve batching
|
||||
// NOTE: This can be done independent of views by reasonably assuming that all 2D views look along the negative-z axis in world space
|
||||
|
@ -476,7 +506,24 @@ pub fn queue_sprites(
|
|||
});
|
||||
let image_bind_groups = &mut *image_bind_groups;
|
||||
|
||||
for (visible_entities, mut transparent_phase) in &mut views {
|
||||
for (mut transparent_phase, visible_entities, view, tonemapping) in &mut views {
|
||||
let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key;
|
||||
if let Some(tonemapping) = tonemapping {
|
||||
if tonemapping.is_enabled && !view.hdr {
|
||||
view_key |= SpritePipelineKey::TONEMAP_IN_SHADER;
|
||||
}
|
||||
}
|
||||
let pipeline = pipelines.specialize(
|
||||
&mut pipeline_cache,
|
||||
&sprite_pipeline,
|
||||
view_key | SpritePipelineKey::from_colored(false),
|
||||
);
|
||||
let colored_pipeline = pipelines.specialize(
|
||||
&mut pipeline_cache,
|
||||
&sprite_pipeline,
|
||||
view_key | SpritePipelineKey::from_colored(true),
|
||||
);
|
||||
|
||||
view_entities.clear();
|
||||
view_entities.extend(visible_entities.entities.iter().map(|e| e.id() as usize));
|
||||
transparent_phase.items.reserve(extracted_sprites.len());
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#ifdef TONEMAP_IN_SHADER
|
||||
#import bevy_core_pipeline::tonemapping
|
||||
#endif
|
||||
|
||||
struct View {
|
||||
view_proj: mat4x4<f32>,
|
||||
inverse_view_proj: mat4x4<f32>,
|
||||
|
@ -48,5 +52,10 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
#ifdef COLORED
|
||||
color = in.color * color;
|
||||
#endif
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
color = vec4<f32>(reinhard_luminance(color.rgb), color.a);
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
|
|
|
@ -115,6 +115,18 @@ pub fn build_ui_render(app: &mut App) {
|
|||
RunGraphOnViewNode::IN_VIEW,
|
||||
)
|
||||
.unwrap();
|
||||
graph_2d
|
||||
.add_node_edge(
|
||||
bevy_core_pipeline::core_2d::graph::node::TONEMAPPING,
|
||||
draw_ui_graph::node::UI_PASS,
|
||||
)
|
||||
.unwrap();
|
||||
graph_2d
|
||||
.add_node_edge(
|
||||
draw_ui_graph::node::UI_PASS,
|
||||
bevy_core_pipeline::core_2d::graph::node::UPSCALING,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(graph_3d) = graph.get_sub_graph_mut(bevy_core_pipeline::core_3d::graph::NAME) {
|
||||
|
@ -129,6 +141,18 @@ pub fn build_ui_render(app: &mut App) {
|
|||
draw_ui_graph::node::UI_PASS,
|
||||
)
|
||||
.unwrap();
|
||||
graph_3d
|
||||
.add_node_edge(
|
||||
bevy_core_pipeline::core_3d::graph::node::TONEMAPPING,
|
||||
draw_ui_graph::node::UI_PASS,
|
||||
)
|
||||
.unwrap();
|
||||
graph_3d
|
||||
.add_node_edge(
|
||||
draw_ui_graph::node::UI_PASS,
|
||||
bevy_core_pipeline::core_3d::graph::node::UPSCALING,
|
||||
)
|
||||
.unwrap();
|
||||
graph_3d
|
||||
.add_slot_edge(
|
||||
graph_3d.input_node().unwrap().id,
|
||||
|
@ -258,6 +282,7 @@ pub fn extract_default_ui_camera_view<T: Component>(
|
|||
0.0,
|
||||
UI_CAMERA_FAR + UI_CAMERA_TRANSFORM_OFFSET,
|
||||
),
|
||||
hdr: camera.hdr,
|
||||
viewport: UVec4::new(
|
||||
physical_origin.x,
|
||||
physical_origin.y,
|
||||
|
|
|
@ -1,29 +1,17 @@
|
|||
use bevy_ecs::{prelude::*, system::SystemState};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::{
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue, RenderTextureFormat},
|
||||
texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo},
|
||||
view::ViewUniform,
|
||||
render_resource::*, renderer::RenderDevice, texture::BevyDefault, view::ViewUniform,
|
||||
};
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct UiPipeline {
|
||||
pub view_layout: BindGroupLayout,
|
||||
pub image_layout: BindGroupLayout,
|
||||
pub dummy_white_gpu_image: GpuImage,
|
||||
}
|
||||
|
||||
impl FromWorld for UiPipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let mut system_state: SystemState<(
|
||||
Res<RenderDevice>,
|
||||
Res<DefaultImageSampler>,
|
||||
Res<RenderQueue>,
|
||||
Res<RenderTextureFormat>,
|
||||
)> = SystemState::new(world);
|
||||
let (render_device, default_sampler, render_queue, first_available_texture_format) =
|
||||
system_state.get_mut(world);
|
||||
let render_device = world.resource::<RenderDevice>();
|
||||
|
||||
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
|
@ -60,57 +48,10 @@ impl FromWorld for UiPipeline {
|
|||
],
|
||||
label: Some("ui_image_layout"),
|
||||
});
|
||||
let dummy_white_gpu_image = {
|
||||
let image = Image::new_fill(
|
||||
Extent3d::default(),
|
||||
TextureDimension::D2,
|
||||
&[255u8; 4],
|
||||
first_available_texture_format.0,
|
||||
);
|
||||
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||
let sampler = match image.sampler_descriptor {
|
||||
ImageSampler::Default => (**default_sampler).clone(),
|
||||
ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor),
|
||||
};
|
||||
|
||||
let format_size = image.texture_descriptor.format.pixel_size();
|
||||
render_queue.write_texture(
|
||||
ImageCopyTexture {
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
origin: Origin3d::ZERO,
|
||||
aspect: TextureAspect::All,
|
||||
},
|
||||
&image.data,
|
||||
ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(
|
||||
std::num::NonZeroU32::new(
|
||||
image.texture_descriptor.size.width * format_size as u32,
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
rows_per_image: None,
|
||||
},
|
||||
image.texture_descriptor.size,
|
||||
);
|
||||
let texture_view = texture.create_view(&TextureViewDescriptor::default());
|
||||
GpuImage {
|
||||
texture,
|
||||
texture_view,
|
||||
texture_format: image.texture_descriptor.format,
|
||||
sampler,
|
||||
size: Vec2::new(
|
||||
image.texture_descriptor.size.width as f32,
|
||||
image.texture_descriptor.size.height as f32,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
UiPipeline {
|
||||
view_layout,
|
||||
image_layout,
|
||||
dummy_white_gpu_image,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +88,7 @@ impl SpecializedRenderPipeline for UiPipeline {
|
|||
shader_defs,
|
||||
entry_point: "fragment".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: self.dummy_white_gpu_image.texture_format,
|
||||
format: TextureFormat::bevy_default(),
|
||||
blend: Some(BlendState::ALPHA_BLENDING),
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
|
|
|
@ -7,9 +7,7 @@ use bevy_ecs::{
|
|||
use bevy_render::{
|
||||
render_graph::*,
|
||||
render_phase::*,
|
||||
render_resource::{
|
||||
CachedRenderPipelineId, LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor,
|
||||
},
|
||||
render_resource::{CachedRenderPipelineId, LoadOp, Operations, RenderPassDescriptor},
|
||||
renderer::*,
|
||||
view::*,
|
||||
};
|
||||
|
@ -81,14 +79,10 @@ impl Node for UiPassNode {
|
|||
};
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("ui_pass"),
|
||||
color_attachments: &[Some(RenderPassColorAttachment {
|
||||
view: &target.view,
|
||||
resolve_target: None,
|
||||
ops: Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
color_attachments: &[Some(target.get_unsampled_color_attachment(Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
}))],
|
||||
depth_stencil_attachment: None,
|
||||
};
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ use bevy::{
|
|||
TextureFormat, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode,
|
||||
},
|
||||
texture::BevyDefault,
|
||||
view::VisibleEntities,
|
||||
view::{ExtractedView, ViewTarget, VisibleEntities},
|
||||
Extract, RenderApp, RenderStage,
|
||||
},
|
||||
sprite::{
|
||||
|
@ -146,6 +146,11 @@ impl SpecializedRenderPipeline for ColoredMesh2dPipeline {
|
|||
let vertex_layout =
|
||||
VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, formats);
|
||||
|
||||
let format = match key.contains(Mesh2dPipelineKey::HDR) {
|
||||
true => ViewTarget::TEXTURE_FORMAT_HDR,
|
||||
false => TextureFormat::bevy_default(),
|
||||
};
|
||||
|
||||
RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
// Use our custom shader
|
||||
|
@ -161,7 +166,7 @@ impl SpecializedRenderPipeline for ColoredMesh2dPipeline {
|
|||
shader_defs: Vec::new(),
|
||||
entry_point: "fragment".into(),
|
||||
targets: vec![Some(ColorTargetState {
|
||||
format: TextureFormat::bevy_default(),
|
||||
format,
|
||||
blend: Some(BlendState::ALPHA_BLENDING),
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
|
@ -311,19 +316,24 @@ pub fn queue_colored_mesh2d(
|
|||
msaa: Res<Msaa>,
|
||||
render_meshes: Res<RenderAssets<Mesh>>,
|
||||
colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With<ColoredMesh2d>>,
|
||||
mut views: Query<(&VisibleEntities, &mut RenderPhase<Transparent2d>)>,
|
||||
mut views: Query<(
|
||||
&VisibleEntities,
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
&ExtractedView,
|
||||
)>,
|
||||
) {
|
||||
if colored_mesh2d.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Iterate each view (a camera is a view)
|
||||
for (visible_entities, mut transparent_phase) in &mut views {
|
||||
for (visible_entities, mut transparent_phase, view) in &mut views {
|
||||
let draw_colored_mesh2d = transparent_draw_functions
|
||||
.read()
|
||||
.get_id::<DrawColoredMesh2d>()
|
||||
.unwrap();
|
||||
|
||||
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples);
|
||||
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples)
|
||||
| Mesh2dPipelineKey::from_hdr(view.hdr);
|
||||
|
||||
// Queue all entities visible to that view
|
||||
for visible_entity in &visible_entities.entities {
|
||||
|
|
Loading…
Reference in a new issue