diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index e3b230a1e7e20..c49c8634b30f7 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -11,6 +11,8 @@ pub mod graph { } } +use std::cmp::Reverse; + pub use camera_3d::*; pub use main_pass_3d_node::*; @@ -87,11 +89,12 @@ pub struct Opaque3d { } impl PhaseItem for Opaque3d { - type SortKey = FloatOrd; + // NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort. + type SortKey = Reverse; #[inline] fn sort_key(&self) -> Self::SortKey { - FloatOrd(self.distance) + Reverse(FloatOrd(self.distance)) } #[inline] @@ -101,7 +104,8 @@ impl PhaseItem for Opaque3d { #[inline] fn sort(items: &mut [Self]) { - radsort::sort_by_key(items, |item| item.distance); + // Key negated to match reversed SortKey ordering + radsort::sort_by_key(items, |item| -item.distance); } } @@ -127,11 +131,12 @@ pub struct AlphaMask3d { } impl PhaseItem for AlphaMask3d { - type SortKey = FloatOrd; + // NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort. + type SortKey = Reverse; #[inline] fn sort_key(&self) -> Self::SortKey { - FloatOrd(self.distance) + Reverse(FloatOrd(self.distance)) } #[inline] @@ -141,7 +146,8 @@ impl PhaseItem for AlphaMask3d { #[inline] fn sort(items: &mut [Self]) { - radsort::sort_by_key(items, |item| item.distance); + // Key negated to match reversed SortKey ordering + radsort::sort_by_key(items, |item| -item.distance); } } @@ -167,6 +173,7 @@ pub struct Transparent3d { } impl PhaseItem for Transparent3d { + // NOTE: Values increase towards the camera. Back-to-front ordering for transparent means we need an ascending sort. type SortKey = FloatOrd; #[inline] diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index a21aed617b24c..b2581a9b2a161 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -348,8 +348,7 @@ pub fn queue_material_meshes( .get_id::>() .unwrap(); - let inverse_view_matrix = view.transform.compute_matrix().inverse(); - let inverse_view_row_2 = inverse_view_matrix.row(2); + let rangefinder = view.rangefinder3d(); let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); for visible_entity in &visible_entities.entities { @@ -383,9 +382,7 @@ pub fn queue_material_meshes( } }; - // NOTE: row 2 of the inverse view matrix dotted with column 3 of the model matrix - // gives the z component of translation of the mesh in view space - let mesh_z = inverse_view_row_2.dot(mesh_uniform.transform.col(3)) + let distance = rangefinder.distance(&mesh_uniform.transform) + material.properties.depth_bias; match alpha_mode { AlphaMode::Opaque => { @@ -393,11 +390,7 @@ pub fn queue_material_meshes( entity: *visible_entity, draw_function: draw_opaque_pbr, pipeline: pipeline_id, - // NOTE: Front-to-back ordering for opaque with ascending sort means near should have the - // lowest sort key and getting further away should increase. As we have - // -z in front of the camera, values in view space decrease away from the - // camera. Flipping the sign of mesh_z results in the correct front-to-back ordering - distance: -mesh_z, + distance, }); } AlphaMode::Mask(_) => { @@ -405,11 +398,7 @@ pub fn queue_material_meshes( entity: *visible_entity, draw_function: draw_alpha_mask_pbr, pipeline: pipeline_id, - // NOTE: Front-to-back ordering for alpha mask with ascending sort means near should have the - // lowest sort key and getting further away should increase. As we have - // -z in front of the camera, values in view space decrease away from the - // camera. Flipping the sign of mesh_z results in the correct front-to-back ordering - distance: -mesh_z, + distance, }); } AlphaMode::Blend => { @@ -417,11 +406,7 @@ pub fn queue_material_meshes( entity: *visible_entity, draw_function: draw_transparent_pbr, pipeline: pipeline_id, - // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the - // lowest sort key and getting closer should increase. As we have - // -z in front of the camera, the largest distance is -far with values increasing toward the - // camera. As such we can just use mesh_z as the distance - distance: mesh_z, + distance, }); } } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index c16e396f8ea6f..6698959b9325d 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -119,8 +119,7 @@ fn queue_wireframes( .unwrap(); let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); for (view, visible_entities, mut opaque_phase) in views.iter_mut() { - let view_matrix = view.transform.compute_matrix(); - let view_row_2 = view_matrix.row(2); + let rangefinder = view.rangefinder3d(); let add_render_phase = |(entity, mesh_handle, mesh_uniform): (Entity, &Handle, &MeshUniform)| { @@ -144,7 +143,7 @@ fn queue_wireframes( entity, pipeline: pipeline_id, draw_function: draw_custom, - distance: view_row_2.dot(mesh_uniform.transform.col(3)), + distance: rangefinder.distance(&mesh_uniform.transform), }); } }; diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 0caa1a75bc29c..b69eb85c35c17 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -6,6 +6,7 @@ pub mod extract_component; pub mod extract_resource; pub mod mesh; pub mod primitives; +pub mod rangefinder; pub mod render_asset; pub mod render_graph; pub mod render_phase; diff --git a/crates/bevy_render/src/rangefinder.rs b/crates/bevy_render/src/rangefinder.rs new file mode 100644 index 0000000000000..c11b8e679d4c7 --- /dev/null +++ b/crates/bevy_render/src/rangefinder.rs @@ -0,0 +1,41 @@ +use bevy_math::{Mat4, Vec4}; + +/// A distance calculator for the draw order of [`PhaseItem`](crate::render_phase::PhaseItem)s. +pub struct ViewRangefinder3d { + inverse_view_row_2: Vec4, +} + +impl ViewRangefinder3d { + /// Creates a 3D rangefinder for a view matrix + pub fn from_view_matrix(view_matrix: &Mat4) -> ViewRangefinder3d { + let inverse_view_matrix = view_matrix.inverse(); + ViewRangefinder3d { + inverse_view_row_2: inverse_view_matrix.row(2), + } + } + + /// Calculates the distance, or view-space Z value, for a transform + #[inline] + pub fn distance(&self, transform: &Mat4) -> f32 { + // NOTE: row 2 of the inverse view matrix dotted with column 3 of the model matrix + // gives the z component of translation of the mesh in view-space + self.inverse_view_row_2.dot(transform.col(3)) + } +} + +#[cfg(test)] +mod tests { + use super::ViewRangefinder3d; + use bevy_math::{Mat4, Vec3}; + + #[test] + fn distance() { + let view_matrix = Mat4::from_translation(Vec3::new(0.0, 0.0, -1.0)); + let rangefinder = ViewRangefinder3d::from_view_matrix(&view_matrix); + assert_eq!(rangefinder.distance(&Mat4::IDENTITY), 1.0); + assert_eq!( + rangefinder.distance(&Mat4::from_translation(Vec3::new(0.0, 0.0, 1.0))), + 2.0 + ); + } +} diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index e1e3f311fe91b..155307475a782 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -12,6 +12,7 @@ use crate::{ camera::ExtractedCamera, extract_resource::{ExtractResource, ExtractResourcePlugin}, prelude::Image, + rangefinder::ViewRangefinder3d, render_asset::RenderAssets, render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView}, renderer::{RenderDevice, RenderQueue}, @@ -84,6 +85,13 @@ pub struct ExtractedView { pub height: u32, } +impl ExtractedView { + /// Creates a 3D rangefinder for a view + pub fn rangefinder3d(&self) -> ViewRangefinder3d { + ViewRangefinder3d::from_view_matrix(&self.transform.compute_matrix()) + } +} + #[derive(Clone, ShaderType)] pub struct ViewUniform { view_proj: Mat4, diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index ef0647cc31acf..d06e0f43f648c 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -116,8 +116,7 @@ fn queue_custom( | MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList); for (view, mut transparent_phase) in views.iter_mut() { - let view_matrix = view.transform.compute_matrix(); - let view_row_2 = view_matrix.row(2); + let rangefinder = view.rangefinder3d(); for (entity, mesh_uniform, mesh_handle) in material_meshes.iter() { if let Some(mesh) = render_meshes.get(mesh_handle) { let pipeline = pipelines @@ -127,7 +126,7 @@ fn queue_custom( entity, pipeline, draw_function: draw_custom, - distance: view_row_2.dot(mesh_uniform.transform.col(3)), + distance: rangefinder.distance(&mesh_uniform.transform), }); } } diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 6bf6858678929..e497920629b4c 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -117,8 +117,7 @@ fn queue_custom( let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); for (view, mut transparent_phase) in views.iter_mut() { - let view_matrix = view.transform.compute_matrix(); - let view_row_2 = view_matrix.row(2); + let rangefinder = view.rangefinder3d(); for (entity, mesh_uniform, mesh_handle) in material_meshes.iter() { if let Some(mesh) = meshes.get(mesh_handle) { let key = @@ -130,7 +129,7 @@ fn queue_custom( entity, pipeline, draw_function: draw_custom, - distance: view_row_2.dot(mesh_uniform.transform.col(3)), + distance: rangefinder.distance(&mesh_uniform.transform), }); } }