Skip to content

Commit 4a9932f

Browse files
committed
simplified API to get NDC from camera and world position (#4041)
# Objective - After #3412, `Camera::world_to_screen` got a little bit uglier to use by needing to provide both `Windows` and `Assets<Image>`, even though only one would be needed https://github.com/bevyengine/bevy/blob/b697e73c3d861c209152ccfb140ae00fbc6e9925/crates/bevy_render/src/camera/camera.rs#L117-L123 - Some time, exact coordinates are not needed but normalized device coordinates is enough ## Solution - Add a function to just get NDC
1 parent 2b6e67f commit 4a9932f

File tree

1 file changed

+25
-7
lines changed

1 file changed

+25
-7
lines changed

crates/bevy_render/src/camera/camera.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ impl Default for DepthCalculation {
122122

123123
impl Camera {
124124
/// Given a position in world space, use the camera to compute the screen space coordinates.
125+
///
126+
/// To get the coordinates in Normalized Device Coordinates, you should use
127+
/// [`world_to_ndc`](Self::world_to_ndc).
125128
pub fn world_to_screen(
126129
&self,
127130
windows: &Windows,
@@ -130,18 +133,33 @@ impl Camera {
130133
world_position: Vec3,
131134
) -> Option<Vec2> {
132135
let window_size = self.target.get_logical_size(windows, images)?;
133-
// Build a transform to convert from world to NDC using camera data
134-
let world_to_ndc: Mat4 =
135-
self.projection_matrix * camera_transform.compute_matrix().inverse();
136-
let ndc_space_coords: Vec3 = world_to_ndc.project_point3(world_position);
136+
let ndc_space_coords = self.world_to_ndc(camera_transform, world_position)?;
137137
// NDC z-values outside of 0 < z < 1 are outside the camera frustum and are thus not in screen space
138138
if ndc_space_coords.z < 0.0 || ndc_space_coords.z > 1.0 {
139139
return None;
140140
}
141+
141142
// Once in NDC space, we can discard the z element and rescale x/y to fit the screen
142-
let screen_space_coords = (ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * window_size;
143-
if !screen_space_coords.is_nan() {
144-
Some(screen_space_coords)
143+
Some((ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * window_size)
144+
}
145+
146+
/// Given a position in world space, use the camera to compute the Normalized Device Coordinates.
147+
///
148+
/// Values returned will be between -1.0 and 1.0 when the position is in screen space.
149+
/// To get the coordinates in the render target dimensions, you should use
150+
/// [`world_to_screen`](Self::world_to_screen).
151+
pub fn world_to_ndc(
152+
&self,
153+
camera_transform: &GlobalTransform,
154+
world_position: Vec3,
155+
) -> Option<Vec3> {
156+
// Build a transform to convert from world to NDC using camera data
157+
let world_to_ndc: Mat4 =
158+
self.projection_matrix * camera_transform.compute_matrix().inverse();
159+
let ndc_space_coords: Vec3 = world_to_ndc.project_point3(world_position);
160+
161+
if !ndc_space_coords.is_nan() {
162+
Some(ndc_space_coords)
145163
} else {
146164
None
147165
}

0 commit comments

Comments
 (0)