Skip to content
Closed
19 changes: 13 additions & 6 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub mod graph {
}
}

use std::cmp::Reverse;

pub use camera_3d::*;
pub use main_pass_3d_node::*;

Expand Down Expand Up @@ -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<FloatOrd>;

#[inline]
fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance)
Reverse(FloatOrd(self.distance))
}

#[inline]
Expand All @@ -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);
}
}

Expand All @@ -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<FloatOrd>;

#[inline]
fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance)
Reverse(FloatOrd(self.distance))
}

#[inline]
Expand All @@ -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);
}
}

Expand All @@ -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]
Expand Down
25 changes: 5 additions & 20 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,7 @@ pub fn queue_material_meshes<M: Material>(
.get_id::<DrawMaterial<M>>()
.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 {
Expand Down Expand Up @@ -383,45 +382,31 @@ pub fn queue_material_meshes<M: Material>(
}
};

// 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 => {
opaque_phase.add(Opaque3d {
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(_) => {
alpha_mask_phase.add(AlphaMask3d {
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 => {
transparent_phase.add(Transparent3d {
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,
});
}
}
Expand Down
5 changes: 2 additions & 3 deletions crates/bevy_pbr/src/wireframe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,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<Mesh>, &MeshUniform)| {
Expand All @@ -142,7 +141,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),
});
}
};
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
41 changes: 41 additions & 0 deletions crates/bevy_render/src/rangefinder.rs
Original file line number Diff line number Diff line change
@@ -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
);
}
}
8 changes: 8 additions & 0 deletions crates/bevy_render/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -81,6 +82,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,
Expand Down
5 changes: 2 additions & 3 deletions examples/shader/animate_shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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),
});
}
}
Expand Down
5 changes: 2 additions & 3 deletions examples/shader/shader_instancing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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),
});
}
}
Expand Down