Skip to content

Commit

Permalink
Allow any pan/zoom in 2D spatial views (#6089)
Browse files Browse the repository at this point in the history
### What
* Closes #2531
* Closes #2490

By default the bounds are centered:

Before:
<img width="2023" alt="Screenshot 2024-04-24 at 11 46 47"
src="https://github.com/rerun-io/rerun/assets/1148717/0b8678ac-e427-4a5a-a633-011eee239fc8">

After:
<img width="2023" alt="Screenshot 2024-04-24 at 11 45 41"
src="https://github.com/rerun-io/rerun/assets/1148717/fbf6fd94-79a8-4ed2-b324-4f34672f0cb9">

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
* Using examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6089?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6089?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!

- [PR Build Summary](https://build.rerun.io/pr/6089)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)

To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.

---------

Co-authored-by: Andreas Reich <[email protected]>
  • Loading branch information
emilk and Wumpf authored Apr 25, 2024
1 parent b6a5072 commit 10753fa
Show file tree
Hide file tree
Showing 24 changed files with 586 additions and 458 deletions.
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,
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
188 changes: 100 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,74 @@ pub enum Projection {
},
}

impl Projection {
fn projection_from_view(self, resolution_in_pixel: [u32; 2]) -> glam::Mat4 {
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 aspect_ratio = resolution_in_pixel[0] as f32 / 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.
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 +244,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 +264,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 +382,33 @@ 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 projection_from_view = config
.projection_from_view
.projection_from_view(config.resolution_in_pixel);

let tan_half_fov = config.projection_from_view.tan_half_fov();

let pixel_world_size_from_camera_distance = match config.projection_from_view {
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 +455,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

0 comments on commit 10753fa

Please sign in to comment.