mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 22:50:19 +00:00
Split Transmissive3d
into a separate pass
This commit is contained in:
parent
fb490297e9
commit
3bc0a018cd
6 changed files with 193 additions and 25 deletions
|
@ -0,0 +1,106 @@
|
|||
use super::ViewTransmissionTexture;
|
||||
use crate::core_3d::Transmissive3d;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::{
|
||||
camera::ExtractedCamera,
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||
render_phase::RenderPhase,
|
||||
render_resource::{
|
||||
Extent3d, LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor,
|
||||
},
|
||||
renderer::RenderContext,
|
||||
view::{ExtractedView, ViewDepthTexture, ViewTarget},
|
||||
};
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
|
||||
/// A [`Node`] that runs the [`Transmissive3d`] [`RenderPhase`].
|
||||
pub struct MainTransmissivePass3dNode {
|
||||
query: QueryState<
|
||||
(
|
||||
&'static ExtractedCamera,
|
||||
&'static RenderPhase<Transmissive3d>,
|
||||
&'static ViewTarget,
|
||||
&'static ViewTransmissionTexture,
|
||||
&'static ViewDepthTexture,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl FromWorld for MainTransmissivePass3dNode {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
Self {
|
||||
query: world.query_filtered(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for MainTransmissivePass3dNode {
|
||||
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.view_entity();
|
||||
let Ok((
|
||||
camera,
|
||||
transmissive_phase,
|
||||
target,
|
||||
transmission,
|
||||
depth,
|
||||
)) = self.query.get_manual(world, view_entity) else {
|
||||
// No window
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let physical_target_size = camera.physical_target_size.unwrap();
|
||||
render_context.command_encoder().copy_texture_to_texture(
|
||||
target.main_texture().as_image_copy(),
|
||||
transmission.texture.as_image_copy(),
|
||||
Extent3d {
|
||||
width: physical_target_size.x,
|
||||
height: physical_target_size.y,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
|
||||
if !transmissive_phase.items.is_empty() {
|
||||
// Run the transmissive pass, sorted back-to-front
|
||||
// NOTE: Scoped to drop the mutable borrow of render_context
|
||||
#[cfg(feature = "trace")]
|
||||
let _main_transmissive_pass_3d_span = info_span!("main_transmissive_pass_3d").entered();
|
||||
|
||||
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
|
||||
label: Some("main_transmissive_pass_3d"),
|
||||
// NOTE: The transmissive pass loads the color buffer as well as overwriting it where appropriate.
|
||||
color_attachments: &[Some(target.get_color_attachment(Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
}))],
|
||||
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
||||
view: &depth.view,
|
||||
// NOTE: The transmissive main pass loads the depth buffer and possibly overwrites it
|
||||
depth_ops: Some(Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
});
|
||||
|
||||
if let Some(viewport) = camera.viewport.as_ref() {
|
||||
render_pass.set_camera_viewport(viewport);
|
||||
}
|
||||
|
||||
transmissive_phase.render(&mut render_pass, world, view_entity);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,13 +1,10 @@
|
|||
use super::ViewTransmissionTexture;
|
||||
use crate::core_3d::Transparent3d;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::{
|
||||
camera::ExtractedCamera,
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||
render_phase::RenderPhase,
|
||||
render_resource::{
|
||||
Extent3d, LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor,
|
||||
},
|
||||
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor},
|
||||
renderer::RenderContext,
|
||||
view::{ExtractedView, ViewDepthTexture, ViewTarget},
|
||||
};
|
||||
|
@ -21,7 +18,6 @@ pub struct MainTransparentPass3dNode {
|
|||
&'static ExtractedCamera,
|
||||
&'static RenderPhase<Transparent3d>,
|
||||
&'static ViewTarget,
|
||||
&'static ViewTransmissionTexture,
|
||||
&'static ViewDepthTexture,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
|
@ -52,24 +48,12 @@ impl Node for MainTransparentPass3dNode {
|
|||
camera,
|
||||
transparent_phase,
|
||||
target,
|
||||
transmission,
|
||||
depth,
|
||||
)) = self.query.get_manual(world, view_entity) else {
|
||||
// No window
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let physical_target_size = camera.physical_target_size.unwrap();
|
||||
render_context.command_encoder().copy_texture_to_texture(
|
||||
target.main_texture().as_image_copy(),
|
||||
transmission.texture.as_image_copy(),
|
||||
Extent3d {
|
||||
width: physical_target_size.x,
|
||||
height: physical_target_size.y,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
|
||||
if !transparent_phase.items.is_empty() {
|
||||
// Run the transparent pass, sorted back-to-front
|
||||
// NOTE: Scoped to drop the mutable borrow of render_context
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod camera_3d;
|
||||
mod main_opaque_pass_3d_node;
|
||||
mod main_transmissive_pass_3d_node;
|
||||
mod main_transparent_pass_3d_node;
|
||||
|
||||
pub mod graph {
|
||||
|
@ -12,6 +13,7 @@ pub mod graph {
|
|||
pub const PREPASS: &str = "prepass";
|
||||
pub const START_MAIN_PASS: &str = "start_main_pass";
|
||||
pub const MAIN_OPAQUE_PASS: &str = "main_opaque_pass";
|
||||
pub const MAIN_TRANSMISSIVE_PASS: &str = "main_transmissive_pass";
|
||||
pub const MAIN_TRANSPARENT_PASS: &str = "main_transparent_pass";
|
||||
pub const END_MAIN_PASS: &str = "end_main_pass";
|
||||
pub const BLOOM: &str = "bloom";
|
||||
|
@ -53,6 +55,7 @@ use bevy_render::{
|
|||
use bevy_utils::{FloatOrd, HashMap};
|
||||
|
||||
use crate::{
|
||||
core_3d::main_transmissive_pass_3d_node::MainTransmissivePass3dNode,
|
||||
prepass::{node::PrepassNode, DepthPrepass},
|
||||
skybox::SkyboxPlugin,
|
||||
tonemapping::TonemappingNode,
|
||||
|
@ -76,6 +79,7 @@ impl Plugin for Core3dPlugin {
|
|||
render_app
|
||||
.init_resource::<DrawFunctions<Opaque3d>>()
|
||||
.init_resource::<DrawFunctions<AlphaMask3d>>()
|
||||
.init_resource::<DrawFunctions<Transmissive3d>>()
|
||||
.init_resource::<DrawFunctions<Transparent3d>>()
|
||||
.add_systems(ExtractSchedule, extract_core_3d_camera_phases)
|
||||
.add_systems(
|
||||
|
@ -89,6 +93,7 @@ impl Plugin for Core3dPlugin {
|
|||
.after(bevy_render::view::prepare_windows),
|
||||
sort_phase_system::<Opaque3d>.in_set(RenderSet::PhaseSort),
|
||||
sort_phase_system::<AlphaMask3d>.in_set(RenderSet::PhaseSort),
|
||||
sort_phase_system::<Transmissive3d>.in_set(RenderSet::PhaseSort),
|
||||
sort_phase_system::<Transparent3d>.in_set(RenderSet::PhaseSort),
|
||||
),
|
||||
);
|
||||
|
@ -99,6 +104,7 @@ impl Plugin for Core3dPlugin {
|
|||
.add_render_graph_node::<PrepassNode>(CORE_3D, PREPASS)
|
||||
.add_render_graph_node::<EmptyNode>(CORE_3D, START_MAIN_PASS)
|
||||
.add_render_graph_node::<MainOpaquePass3dNode>(CORE_3D, MAIN_OPAQUE_PASS)
|
||||
.add_render_graph_node::<MainTransmissivePass3dNode>(CORE_3D, MAIN_TRANSMISSIVE_PASS)
|
||||
.add_render_graph_node::<MainTransparentPass3dNode>(CORE_3D, MAIN_TRANSPARENT_PASS)
|
||||
.add_render_graph_node::<EmptyNode>(CORE_3D, END_MAIN_PASS)
|
||||
.add_render_graph_node::<TonemappingNode>(CORE_3D, TONEMAPPING)
|
||||
|
@ -110,6 +116,7 @@ impl Plugin for Core3dPlugin {
|
|||
PREPASS,
|
||||
START_MAIN_PASS,
|
||||
MAIN_OPAQUE_PASS,
|
||||
MAIN_TRANSMISSIVE_PASS,
|
||||
MAIN_TRANSPARENT_PASS,
|
||||
END_MAIN_PASS,
|
||||
TONEMAPPING,
|
||||
|
@ -200,6 +207,45 @@ impl CachedRenderPipelinePhaseItem for AlphaMask3d {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Transmissive3d {
|
||||
pub distance: f32,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
pub entity: Entity,
|
||||
pub draw_function: DrawFunctionId,
|
||||
}
|
||||
|
||||
impl PhaseItem for Transmissive3d {
|
||||
// NOTE: Values increase towards the camera. Back-to-front ordering for transmissive means we need an ascending sort.
|
||||
type SortKey = FloatOrd;
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
FloatOrd(self.distance)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
radsort::sort_by_key(items, |item| item.distance);
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Transmissive3d {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Transparent3d {
|
||||
pub distance: f32,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
|
@ -248,6 +294,7 @@ pub fn extract_core_3d_camera_phases(
|
|||
commands.get_or_spawn(entity).insert((
|
||||
RenderPhase::<Opaque3d>::default(),
|
||||
RenderPhase::<AlphaMask3d>::default(),
|
||||
RenderPhase::<Transmissive3d>::default(),
|
||||
RenderPhase::<Transparent3d>::default(),
|
||||
));
|
||||
}
|
||||
|
@ -264,6 +311,7 @@ pub fn prepare_core_3d_depth_textures(
|
|||
(
|
||||
With<RenderPhase<Opaque3d>>,
|
||||
With<RenderPhase<AlphaMask3d>>,
|
||||
With<RenderPhase<Transmissive3d>>,
|
||||
With<RenderPhase<Transparent3d>>,
|
||||
),
|
||||
>,
|
||||
|
@ -330,6 +378,7 @@ pub fn prepare_core_3d_transmission_textures(
|
|||
(
|
||||
With<RenderPhase<Opaque3d>>,
|
||||
With<RenderPhase<AlphaMask3d>>,
|
||||
With<RenderPhase<Transmissive3d>>,
|
||||
With<RenderPhase<Transparent3d>>,
|
||||
),
|
||||
>,
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
|
||||
use bevy_core_pipeline::{
|
||||
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
|
||||
core_3d::{AlphaMask3d, Opaque3d, Transmissive3d, Transparent3d},
|
||||
prepass::NormalPrepass,
|
||||
tonemapping::{DebandDither, Tonemapping},
|
||||
};
|
||||
|
@ -133,6 +133,13 @@ pub trait Material: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'stat
|
|||
0.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Returns whether or not the material has light transmission properties. Transmissive materials use the color
|
||||
/// output from the [`Opaque3d`] pass as an input and therefore must run in a separate [`Transmissive3d`] pass.
|
||||
fn transmissive(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns this material's prepass vertex shader. If [`ShaderRef::Default`] is returned, the default prepass vertex shader
|
||||
/// will be used.
|
||||
fn prepass_vertex_shader() -> ShaderRef {
|
||||
|
@ -193,6 +200,7 @@ where
|
|||
render_app
|
||||
.init_resource::<DrawFunctions<Shadow>>()
|
||||
.add_render_command::<Shadow, DrawPrepass<M>>()
|
||||
.add_render_command::<Transmissive3d, DrawMaterial<M>>()
|
||||
.add_render_command::<Transparent3d, DrawMaterial<M>>()
|
||||
.add_render_command::<Opaque3d, DrawMaterial<M>>()
|
||||
.add_render_command::<AlphaMask3d, DrawMaterial<M>>()
|
||||
|
@ -366,6 +374,7 @@ impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterial
|
|||
pub fn queue_material_meshes<M: Material>(
|
||||
opaque_draw_functions: Res<DrawFunctions<Opaque3d>>,
|
||||
alpha_mask_draw_functions: Res<DrawFunctions<AlphaMask3d>>,
|
||||
transmissive_draw_functions: Res<DrawFunctions<Transmissive3d>>,
|
||||
transparent_draw_functions: Res<DrawFunctions<Transparent3d>>,
|
||||
material_pipeline: Res<MaterialPipeline<M>>,
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
|
||||
|
@ -384,6 +393,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||
Option<&NormalPrepass>,
|
||||
&mut RenderPhase<Opaque3d>,
|
||||
&mut RenderPhase<AlphaMask3d>,
|
||||
&mut RenderPhase<Transmissive3d>,
|
||||
&mut RenderPhase<Transparent3d>,
|
||||
)>,
|
||||
) where
|
||||
|
@ -398,11 +408,13 @@ pub fn queue_material_meshes<M: Material>(
|
|||
normal_prepass,
|
||||
mut opaque_phase,
|
||||
mut alpha_mask_phase,
|
||||
mut transmissive_phase,
|
||||
mut transparent_phase,
|
||||
) in &mut views
|
||||
{
|
||||
let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial<M>>();
|
||||
let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial<M>>();
|
||||
let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial<M>>();
|
||||
let draw_transparent_pbr = transparent_draw_functions.read().id::<DrawMaterial<M>>();
|
||||
|
||||
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
|
||||
|
@ -491,12 +503,21 @@ pub fn queue_material_meshes<M: Material>(
|
|||
+ material.properties.depth_bias;
|
||||
match material.properties.alpha_mode {
|
||||
AlphaMode::Opaque => {
|
||||
opaque_phase.add(Opaque3d {
|
||||
entity: *visible_entity,
|
||||
draw_function: draw_opaque_pbr,
|
||||
pipeline: pipeline_id,
|
||||
distance,
|
||||
});
|
||||
if material.properties.transmissive {
|
||||
transmissive_phase.add(Transmissive3d {
|
||||
entity: *visible_entity,
|
||||
draw_function: draw_transmissive_pbr,
|
||||
pipeline: pipeline_id,
|
||||
distance,
|
||||
});
|
||||
} else {
|
||||
opaque_phase.add(Opaque3d {
|
||||
entity: *visible_entity,
|
||||
draw_function: draw_opaque_pbr,
|
||||
pipeline: pipeline_id,
|
||||
distance,
|
||||
});
|
||||
}
|
||||
}
|
||||
AlphaMode::Mask(_) => {
|
||||
alpha_mask_phase.add(AlphaMask3d {
|
||||
|
@ -532,6 +553,9 @@ pub struct MaterialProperties {
|
|||
/// for meshes with equal depth, to avoid z-fighting.
|
||||
/// The bias is in depth-texture units so large values may be needed to overcome small depth differences.
|
||||
pub depth_bias: f32,
|
||||
/// Whether or not the material has light transmission properties. Transmissive materials use the color
|
||||
/// output from the [`Opaque3d`] pass as an input and therefore must run in a separate [`Transmissive3d`] pass.
|
||||
pub transmissive: bool,
|
||||
}
|
||||
|
||||
/// Data prepared for a [`Material`] instance.
|
||||
|
@ -685,6 +709,7 @@ fn prepare_material<M: Material>(
|
|||
properties: MaterialProperties {
|
||||
alpha_mode: material.alpha_mode(),
|
||||
depth_bias: material.depth_bias(),
|
||||
transmissive: material.transmissive(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -530,4 +530,9 @@ impl Material for StandardMaterial {
|
|||
fn depth_bias(&self) -> f32 {
|
||||
self.depth_bias
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn transmissive(&self) -> bool {
|
||||
self.transmission > 0.0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,6 @@ fn setup(
|
|||
thickness: 1.0,
|
||||
ior: 1.5,
|
||||
perceptual_roughness: 0.12,
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
..default()
|
||||
}),
|
||||
transform: Transform::from_xyz(1.0, 0.0, 0.0),
|
||||
|
|
Loading…
Reference in a new issue