Add depth_ndc_to_view_z for cpu-side (#14590)
# Objective
I want to get the visual depth (after view proj matrix stuff) of the
object beneath my cursor.
Even when having a write-back of the depth texture, you would still need
to convert the NDC depth to a logical value.
## Solution
This is done on shader-side by [this
function](e6261b0f5f/crates/bevy_pbr/src/render/view_transformations.wgsl (L151)),
which I ported over to the cpu-side.
I also added `world_to_viewport_with_depth` to get a `Vec3` instead of
`Vec2`.
---
If anyone knows a smarter solution to get the visual depth instead of
going `screen -> viewport ray -> screen`, please let me know :>
This commit is contained in:
parent
5b29402cc8
commit
7c80ae7313
@ -373,6 +373,39 @@ impl Camera {
|
||||
Some(viewport_position)
|
||||
}
|
||||
|
||||
/// Given a position in world space, use the camera to compute the viewport-space coordinates and depth.
|
||||
///
|
||||
/// To get the coordinates in Normalized Device Coordinates, you should use
|
||||
/// [`world_to_ndc`](Self::world_to_ndc).
|
||||
///
|
||||
/// Returns `None` if any of these conditions occur:
|
||||
/// - The computed coordinates are beyond the near or far plane
|
||||
/// - The logical viewport size cannot be computed. See [`logical_viewport_size`](Camera::logical_viewport_size)
|
||||
/// - The world coordinates cannot be mapped to the Normalized Device Coordinates. See [`world_to_ndc`](Camera::world_to_ndc)
|
||||
/// May also panic if `glam_assert` is enabled. See [`world_to_ndc`](Camera::world_to_ndc).
|
||||
#[doc(alias = "world_to_screen_with_depth")]
|
||||
pub fn world_to_viewport_with_depth(
|
||||
&self,
|
||||
camera_transform: &GlobalTransform,
|
||||
world_position: Vec3,
|
||||
) -> Option<Vec3> {
|
||||
let target_size = self.logical_viewport_size()?;
|
||||
let ndc_space_coords = self.world_to_ndc(camera_transform, world_position)?;
|
||||
// NDC z-values outside of 0 < z < 1 are outside the (implicit) camera frustum and are thus not in viewport-space
|
||||
if ndc_space_coords.z < 0.0 || ndc_space_coords.z > 1.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Stretching ndc depth to value via near plane and negating result to be in positive room again.
|
||||
let depth = -self.depth_ndc_to_view_z(ndc_space_coords.z);
|
||||
|
||||
// Once in NDC space, we can discard the z element and rescale x/y to fit the screen
|
||||
let mut viewport_position = (ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * target_size;
|
||||
// Flip the Y co-ordinate origin from the bottom to the top.
|
||||
viewport_position.y = target_size.y - viewport_position.y;
|
||||
Some(viewport_position.extend(depth))
|
||||
}
|
||||
|
||||
/// Returns a ray originating from the camera, that passes through everything beyond `viewport_position`.
|
||||
///
|
||||
/// The resulting ray starts on the near plane of the camera.
|
||||
@ -478,6 +511,24 @@ impl Camera {
|
||||
|
||||
(!world_space_coords.is_nan()).then_some(world_space_coords)
|
||||
}
|
||||
|
||||
/// Converts the depth in Normalized Device Coordinates
|
||||
/// to linear view z for perspective projections.
|
||||
///
|
||||
/// Note: Depth values in front of the camera will be negative as -z is forward
|
||||
pub fn depth_ndc_to_view_z(&self, ndc_depth: f32) -> f32 {
|
||||
let near = self.clip_from_view().w_axis.z; // [3][2]
|
||||
-near / ndc_depth
|
||||
}
|
||||
|
||||
/// Converts the depth in Normalized Device Coordinates
|
||||
/// to linear view z for orthographic projections.
|
||||
///
|
||||
/// Note: Depth values in front of the camera will be negative as -z is forward
|
||||
pub fn depth_ndc_to_view_z_2d(&self, ndc_depth: f32) -> f32 {
|
||||
-(self.clip_from_view().w_axis.z - ndc_depth) / self.clip_from_view().z_axis.z
|
||||
// [3][2] [2][2]
|
||||
}
|
||||
}
|
||||
|
||||
/// Control how this camera outputs once rendering is completed.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user