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:
Lixou 2024-08-02 17:37:29 +02:00 committed by GitHub
parent 5b29402cc8
commit 7c80ae7313
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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.