Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow any pan/zoom in 2D spatial views #6089

Merged
merged 20 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/re_renderer/src/global_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct FrameUniformBuffer {

/// How many pixels there are per point.
/// I.e. the UI zoom factor
pub pixels_from_point: f32,
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
pub pixels_per_point: f32,

/// (tan(fov_y / 2) * aspect_ratio, tan(fov_y /2)), i.e. half ratio of screen dimension to screen distance in x & y.
/// Both values are set to f32max for orthographic projection
Expand Down
5 changes: 5 additions & 0 deletions crates/re_renderer/src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ pub fn ndc_from_pixel(pixel_coord: glam::Vec2, screen_extent: glam::UVec2) -> gl

/// Defines a transformation from a rectangular region of interest into a rectangular target region.
///
/// This is "pan and scan".
///
/// Transforms the range of `region_of_interest` to the range of `region`.
#[derive(Clone, Debug)]
pub struct RectTransform {
/// The region of the scene that should be visible.
pub region_of_interest: RectF32,

/// The full scene.
pub region: RectF32,
}

Expand Down
190 changes: 102 additions & 88 deletions crates/re_renderer/src/view_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub type SharedViewBuilder = Arc<RwLock<Option<ViewBuilder>>>;

/// Configures the camera placement in the orthographic frustum,
/// as well as the coordinate system convention.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum OrthographicCameraMode {
/// Puts the view space origin into the middle of the screen.
///
Expand All @@ -94,7 +94,7 @@ pub enum OrthographicCameraMode {
}

/// How we project from 3D to 2D.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum Projection {
/// Perspective camera looking along the negative z view space axis.
Perspective {
Expand Down Expand Up @@ -124,6 +124,73 @@ pub enum Projection {
},
}

impl Projection {
emilk marked this conversation as resolved.
Show resolved Hide resolved
fn projection_from_view(self, aspect_ratio: f32) -> glam::Mat4 {
emilk marked this conversation as resolved.
Show resolved Hide resolved
match self {
Projection::Perspective {
vertical_fov,
near_plane_distance,
aspect_ratio,
} => {
// We use infinite reverse-z projection matrix
// * great precision both with floating point and integer: https://developer.nvidia.com/content/depth-precision-visualized
// * no need to worry about far plane
glam::Mat4::perspective_infinite_reverse_rh(
vertical_fov,
aspect_ratio,
near_plane_distance,
)
}
Projection::Orthographic {
camera_mode,
vertical_world_size,
far_plane_distance,
} => {
let horizontal_world_size = vertical_world_size * aspect_ratio;

// Note that we inverse z (by swapping near and far plane) to be consistent with our perspective projection.
match camera_mode {
OrthographicCameraMode::NearPlaneCenter => glam::Mat4::orthographic_rh(
-0.5 * horizontal_world_size,
0.5 * horizontal_world_size,
-0.5 * vertical_world_size,
0.5 * vertical_world_size,
far_plane_distance,
0.0,
),
OrthographicCameraMode::TopLeftCornerAndExtendZ => glam::Mat4::orthographic_rh(
0.0,
horizontal_world_size,
vertical_world_size,
0.0,
far_plane_distance,
-far_plane_distance,
),
}
}
}
}

fn tan_half_fov(&self) -> glam::Vec2 {
match self {
Projection::Perspective {
vertical_fov,
aspect_ratio,
..
} => {
// Calculate ratio between screen size and screen distance.
// Great for getting directions from normalized device coordinates.
// (btw. this is the same as [1.0 / projection_from_view[0].x, 1.0 / projection_from_view[1].y])
glam::vec2(
(vertical_fov * 0.5).tan() * aspect_ratio,
(vertical_fov * 0.5).tan(),
)
}
Projection::Orthographic { .. } => glam::vec2(f32::MAX, f32::MAX), // Can't use infinity in shaders
}
}
}

/// How [`Size::AUTO`] is interpreted.
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand Down Expand Up @@ -176,7 +243,7 @@ pub struct TargetConfiguration {
/// I.e. the UI zoom factor.
/// Note that this does not affect any of the camera & projection properties and is only used
/// whenever point sizes were explicitly specified.
pub pixels_from_point: f32,
pub pixels_per_point: f32,

/// How [`Size::AUTO`] is interpreted.
pub auto_size_config: AutoSizeConfig,
Expand All @@ -196,7 +263,7 @@ impl Default for TargetConfiguration {
aspect_ratio: 1.0,
},
viewport_transformation: RectTransform::IDENTITY,
pixels_from_point: 1.0,
pixels_per_point: 1.0,
auto_size_config: Default::default(),
outline_config: None,
}
Expand Down Expand Up @@ -314,89 +381,36 @@ impl ViewBuilder {
},
);

let (projection_from_view, tan_half_fov, pixel_world_size_from_camera_distance) =
match config.projection_from_view.clone() {
Projection::Perspective {
vertical_fov,
near_plane_distance,
aspect_ratio,
} => {
// We use infinite reverse-z projection matrix
// * great precision both with floating point and integer: https://developer.nvidia.com/content/depth-precision-visualized
// * no need to worry about far plane
let projection_from_view = glam::Mat4::perspective_infinite_reverse_rh(
vertical_fov,
aspect_ratio,
near_plane_distance,
);

// Calculate ratio between screen size and screen distance.
// Great for getting directions from normalized device coordinates.
// (btw. this is the same as [1.0 / projection_from_view[0].x, 1.0 / projection_from_view[1].y])
let tan_half_fov = glam::vec2(
(vertical_fov * 0.5).tan() * aspect_ratio,
(vertical_fov * 0.5).tan(),
);

// Determine how wide a pixel is in world space at unit distance from the camera.
//
// derivation:
// tan(FOV / 2) = (screen_in_world / 2) / distance
// screen_in_world = tan(FOV / 2) * distance * 2
//
// want: pixels in world per distance, i.e (screen_in_world / resolution / distance)
// => (resolution / screen_in_world / distance) = tan(FOV / 2) * distance * 2 / resolution / distance =
// = tan(FOV / 2) * 2.0 / resolution
let pixel_world_size_from_camera_distance =
tan_half_fov.y * 2.0 / config.resolution_in_pixel[1] as f32;

(
projection_from_view,
tan_half_fov,
pixel_world_size_from_camera_distance,
)
}
Projection::Orthographic {
camera_mode,
vertical_world_size,
far_plane_distance,
} => {
let aspect_ratio =
config.resolution_in_pixel[0] as f32 / config.resolution_in_pixel[1] as f32;
let horizontal_world_size = vertical_world_size * aspect_ratio;
// Note that we inverse z (by swapping near and far plane) to be consistent with our perspective projection.
let projection_from_view = match camera_mode {
OrthographicCameraMode::NearPlaneCenter => glam::Mat4::orthographic_rh(
-0.5 * horizontal_world_size,
0.5 * horizontal_world_size,
-0.5 * vertical_world_size,
0.5 * vertical_world_size,
far_plane_distance,
0.0,
),
OrthographicCameraMode::TopLeftCornerAndExtendZ => {
glam::Mat4::orthographic_rh(
0.0,
horizontal_world_size,
vertical_world_size,
0.0,
far_plane_distance,
-far_plane_distance,
)
}
};

let tan_half_fov = glam::vec2(f32::MAX, f32::MAX);
let pixel_world_size_from_camera_distance = vertical_world_size
/ config.resolution_in_pixel[0].max(config.resolution_in_pixel[1]) as f32;

(
projection_from_view,
tan_half_fov,
pixel_world_size_from_camera_distance,
)
}
};
let aspect_ratio =
config.resolution_in_pixel[0] as f32 / config.resolution_in_pixel[1] as f32;

let projection_from_view = config
.projection_from_view
.projection_from_view(aspect_ratio);

let tan_half_fov = config.projection_from_view.tan_half_fov();

let pixel_world_size_from_camera_distance = match config.projection_from_view {
emilk marked this conversation as resolved.
Show resolved Hide resolved
Projection::Perspective { .. } => {
// Determine how wide a pixel is in world space at unit distance from the camera.
//
// derivation:
// tan(FOV / 2) = (screen_in_world / 2) / distance
// screen_in_world = tan(FOV / 2) * distance * 2
//
// want: pixels in world per distance, i.e (screen_in_world / resolution / distance)
// => (resolution / screen_in_world / distance) = tan(FOV / 2) * distance * 2 / resolution / distance =
// = tan(FOV / 2) * 2.0 / resolution
tan_half_fov.y * 2.0 / config.resolution_in_pixel[1] as f32
}
Projection::Orthographic {
vertical_world_size,
..
} => {
vertical_world_size
/ config.resolution_in_pixel[0].max(config.resolution_in_pixel[1]) as f32
}
};

// Finally, apply a viewport transformation to the projection.
let ndc_scale_and_translation = config
Expand Down Expand Up @@ -443,7 +457,7 @@ impl ViewBuilder {
camera_forward,
tan_half_fov: tan_half_fov.into(),
pixel_world_size_from_camera_distance,
pixels_from_point: config.pixels_from_point,
pixels_per_point: config.pixels_per_point,

auto_size_points: auto_size_points.0,
auto_size_lines: auto_size_lines.0,
Expand Down
6 changes: 3 additions & 3 deletions crates/re_renderer_examples/2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl framework::Example for Render2D {
re_ctx: &re_renderer::RenderContext,
resolution: [u32; 2],
time: &framework::Time,
pixels_from_point: f32,
pixels_per_point: f32,
) -> Vec<framework::ViewDrawResult> {
let splits = framework::split_resolution(resolution, 1, 2).collect::<Vec<_>>();

Expand Down Expand Up @@ -302,7 +302,7 @@ impl framework::Example for Render2D {
vertical_world_size: splits[0].resolution_in_pixel[1] as f32,
far_plane_distance: 1000.0,
},
pixels_from_point,
pixels_per_point,
..Default::default()
},
);
Expand Down Expand Up @@ -344,7 +344,7 @@ impl framework::Example for Render2D {
near_plane_distance: 0.01,
aspect_ratio: resolution[0] as f32 / resolution[1] as f32,
},
pixels_from_point,
pixels_per_point,
..Default::default()
},
);
Expand Down
14 changes: 7 additions & 7 deletions crates/re_renderer_examples/depth_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl RenderDepthClouds {
fn draw_backprojected_point_cloud<FD, ID>(
&mut self,
re_ctx: &re_renderer::RenderContext,
pixels_from_point: f32,
pixels_per_point: f32,
resolution_in_pixel: [u32; 2],
target_location: glam::Vec2,
frame_draw_data: FD,
Expand Down Expand Up @@ -122,7 +122,7 @@ impl RenderDepthClouds {
near_plane_distance: 0.01,
aspect_ratio: resolution_in_pixel[0] as f32 / resolution_in_pixel[1] as f32,
},
pixels_from_point,
pixels_per_point,
..Default::default()
},
);
Expand All @@ -146,7 +146,7 @@ impl RenderDepthClouds {
fn draw_depth_cloud<FD, ID>(
&mut self,
re_ctx: &re_renderer::RenderContext,
pixels_from_point: f32,
pixels_per_point: f32,
resolution_in_pixel: [u32; 2],
target_location: glam::Vec2,
frame_draw_data: FD,
Expand Down Expand Up @@ -202,7 +202,7 @@ impl RenderDepthClouds {
near_plane_distance: 0.01,
aspect_ratio: resolution_in_pixel[0] as f32 / resolution_in_pixel[1] as f32,
},
pixels_from_point,
pixels_per_point,
..Default::default()
},
);
Expand Down Expand Up @@ -265,7 +265,7 @@ impl framework::Example for RenderDepthClouds {
re_ctx: &re_renderer::RenderContext,
resolution: [u32; 2],
time: &framework::Time,
pixels_from_point: f32,
pixels_per_point: f32,
) -> Vec<framework::ViewDrawResult> {
let Self {
albedo,
Expand Down Expand Up @@ -329,15 +329,15 @@ impl framework::Example for RenderDepthClouds {
vec![
self.draw_backprojected_point_cloud(
re_ctx,
pixels_from_point,
pixels_per_point,
splits[0].resolution_in_pixel,
splits[0].target_location,
frame_draw_data.clone(),
image_draw_data.clone(),
),
self.draw_depth_cloud(
re_ctx,
pixels_from_point,
pixels_per_point,
splits[1].resolution_in_pixel,
splits[1].target_location,
frame_draw_data,
Expand Down
4 changes: 2 additions & 2 deletions crates/re_renderer_examples/depth_offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl framework::Example for Render2D {
re_ctx: &re_renderer::RenderContext,
resolution: [u32; 2],
_time: &framework::Time,
pixels_from_point: f32,
pixels_per_point: f32,
) -> Vec<framework::ViewDrawResult> {
let mut rectangles = Vec::new();

Expand Down Expand Up @@ -110,7 +110,7 @@ impl framework::Example for Render2D {
near_plane_distance: self.near_plane,
aspect_ratio: resolution[0] as f32 / resolution[1] as f32,
},
pixels_from_point,
pixels_per_point,
..Default::default()
},
);
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer_examples/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub trait Example {
re_ctx: &RenderContext,
resolution: [u32; 2],
time: &Time,
pixels_from_point: f32,
pixels_per_point: f32,
) -> Vec<ViewDrawResult>;

fn on_key_event(&mut self, _event: winit::event::KeyEvent) {}
Expand Down
Loading
Loading