From 273fc3bc71ec1666d575324a527867252c72327e Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 1 Nov 2024 12:11:26 +0100 Subject: [PATCH 01/14] round joints without breaking existing line drawing features --- crates/viewer/re_renderer/shader/lines.wgsl | 49 +++++++++++++++++-- .../viewer/re_renderer/src/renderer/lines.rs | 2 + 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/crates/viewer/re_renderer/shader/lines.wgsl b/crates/viewer/re_renderer/shader/lines.wgsl index cec58b8232c8..591154b45959 100644 --- a/crates/viewer/re_renderer/shader/lines.wgsl +++ b/crates/viewer/re_renderer/shader/lines.wgsl @@ -47,6 +47,9 @@ const FLAG_CAP_START_EXTEND_OUTWARDS: u32 = 32u; const FLAG_COLOR_GRADIENT: u32 = 64u; const FLAG_FORCE_ORTHO_SPANNING: u32 = 128u; +// Additional flags computed by the shader. +const FLAG_ROUND_JOINT: u32 = 1024u; + // A lot of the attributes don't need to be interpolated across triangles. // To document that and safe some time we mark them up with @interpolate(flat) // (see https://www.w3.org/TR/WGSL/#interpolation) @@ -74,6 +77,12 @@ struct VertexOut { @location(6) @interpolate(flat) picking_instance_id: vec2u, + + // TODO: compute some of the above from this: + @location(7) @interpolate(flat) + center_position_begin: vec3f, + @location(8) @interpolate(flat) + center_position_end: vec3f, }; struct LineStripData { @@ -166,6 +175,8 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { // If the strip indices don't match up for start/end, then we're in a cap triangle! let is_cap_triangle = pos_data_quad_begin.strip_index != pos_data_quad_end.strip_index; + let is_first_quad_after_cap = !is_cap_triangle && (pos_data_quad_begin.strip_index != pos_data_quad_before.strip_index); + let is_last_quad_before_cap = !is_cap_triangle && (pos_data_quad_end.strip_index != pos_data_quad_after.strip_index); // Let's determine which one of the two position data is closer to our vertex. // Which tells us things: @@ -266,6 +277,8 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { let round_cap_circle_center = center_position; var pos: vec3f; + var fragment_flags = strip_data.flags & + (FLAG_COLOR_GRADIENT | (u32(is_cap_triangle) * select(FLAG_CAP_START_ROUND, FLAG_CAP_END_ROUND, is_right_triangle))); if is_cap_triangle && is_at_pointy_end { // We extend the cap triangle far enough to handle triangle caps, // and far enough to do rounded caps without any visible clipping. @@ -277,6 +290,13 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { pos = center_position + (active_radius * top_bottom) * dir_up; } + // Extend the line for rendering smooth joints. + if !is_cap_triangle && + ((is_at_quad_end && !is_last_quad_before_cap) || (!is_at_quad_end && !is_first_quad_after_cap)) { + pos += quad_dir * strip_radius * select(-1.0, 1.0, is_at_quad_end); + fragment_flags |= FLAG_ROUND_JOINT; + } + // Output, transform to projection space and done. var out: VertexOut; out.position = apply_depth_offset(frame.projection_from_world * vec4f(pos, 1.0), batch.depth_offset); @@ -285,13 +305,21 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { out.round_cap_circle_center = round_cap_circle_center; out.color = strip_data.color; out.active_radius = active_radius; - out.fragment_flags = strip_data.flags & - (FLAG_COLOR_GRADIENT | (u32(is_cap_triangle) * select(FLAG_CAP_START_ROUND, FLAG_CAP_END_ROUND, is_right_triangle))); + out.fragment_flags = fragment_flags; out.picking_instance_id = strip_data.picking_instance_id; + out.center_position_begin = pos_data_quad_begin.pos; + out.center_position_end = pos_data_quad_end.pos; return out; } +fn distance_to_line(pos: vec3f, line_a: vec3f, line_b: vec3f) -> f32 { + let pa = pos - line_a; + let ba = line_b - line_a; + let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - ba*h); +} + fn compute_coverage(in: VertexOut) -> f32 { var coverage = 1.0; if has_any_flag(in.fragment_flags, FLAG_CAP_START_ROUND | FLAG_CAP_END_ROUND) { @@ -302,8 +330,23 @@ fn compute_coverage(in: VertexOut) -> f32 { // If we do only outwards, rectangle outlines won't line up nicely let half_pixel_world_size = pixel_world_size * 0.5; let signed_distance_to_border = distance_to_skeleton - in.active_radius; - coverage = 1.0 - saturate((signed_distance_to_border + half_pixel_world_size) / pixel_world_size); + coverage *= 1.0 - saturate((signed_distance_to_border + half_pixel_world_size) / pixel_world_size); } + + // TODO: do this only for joints + if has_any_flag(in.fragment_flags, FLAG_ROUND_JOINT) { + let distance_to_skeleton = distance_to_line(in.position_world, in.center_position_begin, in.center_position_end); + let pixel_world_size = approx_pixel_world_size_at(length(in.position_world - frame.camera_position)); + + // It's important that we do antialias both inwards and outwards of the exact border. + // If we do only outwards, rectangle outlines won't line up nicely + let half_pixel_world_size = pixel_world_size * 0.5; + let signed_distance_to_border = distance_to_skeleton - in.active_radius; + coverage *= 1.0 - saturate((signed_distance_to_border + half_pixel_world_size) / pixel_world_size); + } + + // Debugging hack: An offset here makes the geometry visible! + //return coverage + 0.1; return coverage; } diff --git a/crates/viewer/re_renderer/src/renderer/lines.rs b/crates/viewer/re_renderer/src/renderer/lines.rs index 19409a83383b..d4da3591301e 100644 --- a/crates/viewer/re_renderer/src/renderer/lines.rs +++ b/crates/viewer/re_renderer/src/renderer/lines.rs @@ -39,6 +39,8 @@ //! Why not a triangle *strip* instead if *list*? //! ----------------------------------------------- //! +//! TODO: rewrite this chapter +//! //! As long as we're not able to restart the strip (requires indices!), we can't discard a quad in a triangle strip setup. //! However, this could be solved with an index buffer which has the ability to restart triangle strips (something we haven't tried yet). //! From e85ed480f746d895072b8c1df0e0fc048b796ad9 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 1 Nov 2024 13:15:00 +0100 Subject: [PATCH 02/14] compute round joints & round caps on the same codepath --- crates/viewer/re_renderer/shader/lines.wgsl | 123 +++++++++----------- 1 file changed, 56 insertions(+), 67 deletions(-) diff --git a/crates/viewer/re_renderer/shader/lines.wgsl b/crates/viewer/re_renderer/shader/lines.wgsl index 591154b45959..4933d485ae34 100644 --- a/crates/viewer/re_renderer/shader/lines.wgsl +++ b/crates/viewer/re_renderer/shader/lines.wgsl @@ -63,26 +63,20 @@ struct VertexOut { @location(1) @interpolate(perspective) position_world: vec3f, - @location(2) @interpolate(perspective) - center_position: vec3f, + @location(2) @interpolate(flat) + rounded_inner_line_begin: vec3f, @location(3) @interpolate(flat) - active_radius: f32, + rounded_inner_line_end: vec3f, - @location(4) @interpolate(perspective) - round_cap_circle_center: vec3f, + @location(4) @interpolate(flat) + rounded_inner_line_radius: f32, @location(5) @interpolate(flat) fragment_flags: u32, @location(6) @interpolate(flat) picking_instance_id: vec2u, - - // TODO: compute some of the above from this: - @location(7) @interpolate(flat) - center_position_begin: vec3f, - @location(8) @interpolate(flat) - center_position_end: vec3f, }; struct LineStripData { @@ -200,14 +194,14 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { // Compute quad_dir & correct center_position for triangle caps. var quad_dir: vec3f; - var is_at_pointy_end = false; - let is_end_cap_triangle = is_cap_triangle && is_right_triangle && has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE | FLAG_CAP_END_ROUND); - let is_start_cap_triangle = is_cap_triangle && !is_right_triangle && has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE | FLAG_CAP_START_ROUND); + var is_at_pointy_arrow_end = false; + let is_end_cap_triangle = is_cap_triangle && is_right_triangle && has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE); + let is_start_cap_triangle = is_cap_triangle && !is_right_triangle && has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE); if is_end_cap_triangle { - is_at_pointy_end = is_at_quad_end; + is_at_pointy_arrow_end = is_at_quad_end; quad_dir = pos_data_quad_begin.pos - pos_data_quad_before.pos; // Go one pos data back. } else if is_start_cap_triangle { - is_at_pointy_end = !is_at_quad_end; + is_at_pointy_arrow_end = !is_at_quad_end; quad_dir = pos_data_quad_after.pos - pos_data_quad_end.pos; // Go one pos data forward. } else if is_cap_triangle { // Discard vertex. @@ -237,19 +231,27 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { triangle_cap_length *= triangle_cap_size_factor; // Make space for the end cap if this is either the cap itself or the cap follows right after/before this quad. - if !has_any_flag(strip_data.flags, FLAG_CAP_END_EXTEND_OUTWARDS) && - (is_end_cap_triangle || (is_at_quad_end && pos_data_current.strip_index != pos_data_quad_after.strip_index)) { - var cap_length = - f32(has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND)) * strip_radius + - f32(has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE)) * triangle_cap_length; - center_position -= quad_dir * cap_length; - } - if !has_any_flag(strip_data.flags, FLAG_CAP_START_EXTEND_OUTWARDS) && - (is_start_cap_triangle || (!is_at_quad_end && pos_data_current.strip_index != pos_data_quad_before.strip_index)) { + var rounded_inner_line_begin = pos_data_quad_begin.pos; + if !has_any_flag(strip_data.flags, FLAG_CAP_START_EXTEND_OUTWARDS) && (is_start_cap_triangle || is_first_quad_after_cap) { var cap_length = f32(has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND)) * strip_radius + f32(has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE)) * triangle_cap_length; - center_position += quad_dir * cap_length; + let offset = quad_dir * cap_length; + rounded_inner_line_begin += offset; + if !is_at_quad_end || is_start_cap_triangle { + center_position += offset; + } + } + var rounded_inner_line_end = pos_data_quad_end.pos; + if !has_any_flag(strip_data.flags, FLAG_CAP_END_EXTEND_OUTWARDS) && (is_end_cap_triangle || is_last_quad_before_cap) { + var cap_length = + f32(has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND)) * strip_radius + + f32(has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE)) * triangle_cap_length; + let offset = quad_dir * cap_length; + rounded_inner_line_end -= offset; + if is_at_quad_end || is_end_cap_triangle { + center_position -= offset; + } } // Boost radius only now that we subtracted/added the cap length. @@ -264,36 +266,32 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { center_position += quad_dir * (size_boost * select(-1.0, 1.0, is_at_quad_end)); } - var active_radius = strip_radius; // If this is a triangle cap, we blow up our ("virtual") quad by a given factor. if (is_end_cap_triangle && has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE)) || (is_start_cap_triangle && has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE)) { - active_radius *= batch.triangle_cap_width_factor * triangle_cap_size_factor; + strip_radius *= batch.triangle_cap_width_factor * triangle_cap_size_factor; } // Span up the vertex away from the line's axis, orthogonal to the direction to the camera let dir_up = normalize(cross(camera_ray.direction, quad_dir)); - - let round_cap_circle_center = center_position; - var pos: vec3f; - var fragment_flags = strip_data.flags & - (FLAG_COLOR_GRADIENT | (u32(is_cap_triangle) * select(FLAG_CAP_START_ROUND, FLAG_CAP_END_ROUND, is_right_triangle))); - if is_cap_triangle && is_at_pointy_end { - // We extend the cap triangle far enough to handle triangle caps, - // and far enough to do rounded caps without any visible clipping. - // There is _some_ clipping, but we can't see it ;) - // If we want to do it properly, we would extend the radius for rounded caps too. + if is_cap_triangle && is_at_pointy_arrow_end { + // We extend the cap triangle far enough to handle triangle caps. center_position += quad_dir * (triangle_cap_length * select(-1.0, 1.0, is_right_triangle)); pos = center_position; } else { - pos = center_position + (active_radius * top_bottom) * dir_up; + pos = center_position + (strip_radius * top_bottom * 0.99) * dir_up; } - // Extend the line for rendering smooth joints. - if !is_cap_triangle && - ((is_at_quad_end && !is_last_quad_before_cap) || (!is_at_quad_end && !is_first_quad_after_cap)) { - pos += quad_dir * strip_radius * select(-1.0, 1.0, is_at_quad_end); + // Extend the line for rendering smooth joints, as well as round start/end caps. + var fragment_flags = strip_data.flags & FLAG_COLOR_GRADIENT; + // It's easier to list reasons for not having a joint rather than the other way about when there's _no_ joint: + let no_joint = is_cap_triangle || + (!has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND) && !is_at_quad_end && is_first_quad_after_cap) || + (!has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND) && is_at_quad_end && is_last_quad_before_cap); + if !no_joint { + let left_right_offset = quad_dir * strip_radius * select(-1.0, 1.0, is_at_quad_end); + pos += left_right_offset; fragment_flags |= FLAG_ROUND_JOINT; } @@ -301,48 +299,40 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { var out: VertexOut; out.position = apply_depth_offset(frame.projection_from_world * vec4f(pos, 1.0), batch.depth_offset); out.position_world = pos; - out.center_position = center_position; - out.round_cap_circle_center = round_cap_circle_center; out.color = strip_data.color; - out.active_radius = active_radius; + out.rounded_inner_line_begin = rounded_inner_line_begin; + out.rounded_inner_line_end = rounded_inner_line_end; + out.rounded_inner_line_radius = strip_radius; out.fragment_flags = fragment_flags; out.picking_instance_id = strip_data.picking_instance_id; - out.center_position_begin = pos_data_quad_begin.pos; - out.center_position_end = pos_data_quad_end.pos; return out; } -fn distance_to_line(pos: vec3f, line_a: vec3f, line_b: vec3f) -> f32 { +fn distance_to_line_sq(pos: vec3f, line_a: vec3f, line_b: vec3f) -> f32 { let pa = pos - line_a; let ba = line_b - line_a; let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba*h); + let to_line = pa - ba*h; + return dot(to_line, to_line); +} + +fn distance_to_line(pos: vec3f, line_a: vec3f, line_b: vec3f) -> f32 { + return sqrt(distance_to_line_sq(pos, line_a, line_b)); } fn compute_coverage(in: VertexOut) -> f32 { var coverage = 1.0; - if has_any_flag(in.fragment_flags, FLAG_CAP_START_ROUND | FLAG_CAP_END_ROUND) { - let distance_to_skeleton = length(in.position_world - in.round_cap_circle_center); - let pixel_world_size = approx_pixel_world_size_at(length(in.position_world - frame.camera_position)); - - // It's important that we do antialias both inwards and outwards of the exact border. - // If we do only outwards, rectangle outlines won't line up nicely - let half_pixel_world_size = pixel_world_size * 0.5; - let signed_distance_to_border = distance_to_skeleton - in.active_radius; - coverage *= 1.0 - saturate((signed_distance_to_border + half_pixel_world_size) / pixel_world_size); - } - // TODO: do this only for joints if has_any_flag(in.fragment_flags, FLAG_ROUND_JOINT) { - let distance_to_skeleton = distance_to_line(in.position_world, in.center_position_begin, in.center_position_end); + let distance_to_skeleton = distance_to_line(in.position_world, in.rounded_inner_line_begin, in.rounded_inner_line_end); let pixel_world_size = approx_pixel_world_size_at(length(in.position_world - frame.camera_position)); // It's important that we do antialias both inwards and outwards of the exact border. // If we do only outwards, rectangle outlines won't line up nicely let half_pixel_world_size = pixel_world_size * 0.5; - let signed_distance_to_border = distance_to_skeleton - in.active_radius; - coverage *= 1.0 - saturate((signed_distance_to_border + half_pixel_world_size) / pixel_world_size); + let signed_distance_to_border = distance_to_skeleton - in.rounded_inner_line_radius; + coverage = 1.0 - saturate((signed_distance_to_border + half_pixel_world_size) / pixel_world_size); } // Debugging hack: An offset here makes the geometry visible! @@ -360,9 +350,8 @@ fn fs_main(in: VertexOut) -> @location(0) vec4f { // TODO(andreas): lighting setup var shading = 1.0; if has_any_flag(in.fragment_flags, FLAG_COLOR_GRADIENT) { - let to_center = in.position_world - in.center_position; - let relative_distance_to_center_sq = dot(to_center, to_center) / (in.active_radius * in.active_radius); - shading = max(0.2, 1.0 - relative_distance_to_center_sq) * 0.9; + let distance_to_inner = distance_to_line_sq(in.position_world, in.rounded_inner_line_begin, in.rounded_inner_line_end); + shading = max(0.2, 1.0 - distance_to_inner / (in.rounded_inner_line_radius * in.rounded_inner_line_radius)) * 0.9; } return vec4f(in.color.rgb * shading, coverage); From aa1b89710f96b629746e7333b8b896c00579186f Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 8 Nov 2024 19:34:03 +0100 Subject: [PATCH 03/14] add line radius boost to geo line outlines --- .../re_space_view_map/src/visualizers/geo_line_strings.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs index ba79e05afbf7..fcdbe6b3aac8 100644 --- a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs +++ b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs @@ -136,6 +136,9 @@ impl GeoLineStringsVisualizer { highlight: &SpaceViewHighlights, ) -> Result<(), LineDrawDataError> { let mut lines = re_renderer::LineDrawableBuilder::new(render_ctx); + lines.radius_boost_in_ui_points_for_outlines( + re_space_view::SIZE_BOOST_IN_POINTS_FOR_LINE_OUTLINES, + ); for (entity_path, batch) in &self.batches { let outline = highlight.entity_outline_mask(entity_path.hash()); From d842d6fda5995baa613f4585b67e227493e56346 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 8 Nov 2024 20:07:48 +0100 Subject: [PATCH 04/14] Fix artifacts due to flag int flag flat interpolation --- crates/viewer/re_renderer/shader/lines.wgsl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/viewer/re_renderer/shader/lines.wgsl b/crates/viewer/re_renderer/shader/lines.wgsl index 4933d485ae34..87931e84391a 100644 --- a/crates/viewer/re_renderer/shader/lines.wgsl +++ b/crates/viewer/re_renderer/shader/lines.wgsl @@ -47,8 +47,8 @@ const FLAG_CAP_START_EXTEND_OUTWARDS: u32 = 32u; const FLAG_COLOR_GRADIENT: u32 = 64u; const FLAG_FORCE_ORTHO_SPANNING: u32 = 128u; -// Additional flags computed by the shader. -const FLAG_ROUND_JOINT: u32 = 1024u; +// Special flags used in the fragment shader. +const FLAG_CAP_TRIANGLE: u32 = FLAG_CAP_START_TRIANGLE | FLAG_CAP_END_TRIANGLE; // A lot of the attributes don't need to be interpolated across triangles. // To document that and safe some time we mark them up with @interpolate(flat) @@ -266,10 +266,14 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { center_position += quad_dir * (size_boost * select(-1.0, 1.0, is_at_quad_end)); } + // Filtered list of flats that the fragment shader is interested in. + var fragment_flags = strip_data.flags & FLAG_COLOR_GRADIENT; + // If this is a triangle cap, we blow up our ("virtual") quad by a given factor. if (is_end_cap_triangle && has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE)) || (is_start_cap_triangle && has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE)) { strip_radius *= batch.triangle_cap_width_factor * triangle_cap_size_factor; + fragment_flags |= FLAG_CAP_TRIANGLE; } // Span up the vertex away from the line's axis, orthogonal to the direction to the camera @@ -284,15 +288,11 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { } // Extend the line for rendering smooth joints, as well as round start/end caps. - var fragment_flags = strip_data.flags & FLAG_COLOR_GRADIENT; - // It's easier to list reasons for not having a joint rather than the other way about when there's _no_ joint: - let no_joint = is_cap_triangle || - (!has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND) && !is_at_quad_end && is_first_quad_after_cap) || - (!has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND) && is_at_quad_end && is_last_quad_before_cap); - if !no_joint { + if !is_cap_triangle && + ((!is_at_quad_end && (!is_first_quad_after_cap || has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND))) || + (is_at_quad_end && (!is_last_quad_before_cap || has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND)))) { let left_right_offset = quad_dir * strip_radius * select(-1.0, 1.0, is_at_quad_end); pos += left_right_offset; - fragment_flags |= FLAG_ROUND_JOINT; } // Output, transform to projection space and done. @@ -324,7 +324,7 @@ fn distance_to_line(pos: vec3f, line_a: vec3f, line_b: vec3f) -> f32 { fn compute_coverage(in: VertexOut) -> f32 { var coverage = 1.0; - if has_any_flag(in.fragment_flags, FLAG_ROUND_JOINT) { + if !has_any_flag(in.fragment_flags, FLAG_CAP_TRIANGLE) { let distance_to_skeleton = distance_to_line(in.position_world, in.rounded_inner_line_begin, in.rounded_inner_line_end); let pixel_world_size = approx_pixel_world_size_at(length(in.position_world - frame.camera_position)); From ba6dd0b800d9ce378955233f35597055f1c35fee Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Fri, 8 Nov 2024 20:33:32 +0100 Subject: [PATCH 05/14] make line joint extension code easier to read --- crates/viewer/re_renderer/shader/lines.wgsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/viewer/re_renderer/shader/lines.wgsl b/crates/viewer/re_renderer/shader/lines.wgsl index 87931e84391a..4769ff887a69 100644 --- a/crates/viewer/re_renderer/shader/lines.wgsl +++ b/crates/viewer/re_renderer/shader/lines.wgsl @@ -288,9 +288,10 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { } // Extend the line for rendering smooth joints, as well as round start/end caps. - if !is_cap_triangle && - ((!is_at_quad_end && (!is_first_quad_after_cap || has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND))) || - (is_at_quad_end && (!is_last_quad_before_cap || has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND)))) { + let is_at_inner_joint = !is_cap_triangle && !is_first_quad_after_cap && !is_last_quad_before_cap; + let is_at_quad_with_round_capped_start = !is_at_quad_end && is_first_quad_after_cap && has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND); + let is_at_quad_with_round_capped_end = is_at_quad_end && is_last_quad_before_cap && has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND); + if is_at_inner_joint || is_at_quad_with_round_capped_start || is_at_quad_with_round_capped_end { let left_right_offset = quad_dir * strip_radius * select(-1.0, 1.0, is_at_quad_end); pos += left_right_offset; } From afab8b9c7951ecb67f4d7e3b6f9671aafab1bcd5 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 11:24:24 +0100 Subject: [PATCH 06/14] fixup doc block in lines.rs --- .../viewer/re_renderer/src/renderer/lines.rs | 37 ++----------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/crates/viewer/re_renderer/src/renderer/lines.rs b/crates/viewer/re_renderer/src/renderer/lines.rs index d4da3591301e..5ff42e5efe30 100644 --- a/crates/viewer/re_renderer/src/renderer/lines.rs +++ b/crates/viewer/re_renderer/src/renderer/lines.rs @@ -39,12 +39,10 @@ //! Why not a triangle *strip* instead if *list*? //! ----------------------------------------------- //! -//! TODO: rewrite this chapter -//! //! As long as we're not able to restart the strip (requires indices!), we can't discard a quad in a triangle strip setup. //! However, this could be solved with an index buffer which has the ability to restart triangle strips (something we haven't tried yet). //! -//! Another much more tricky issue is handling of line miters: +//! Another much more tricky issue is handling of line joints: //! Let's have a look at a corner between two line positions (line positions marked with `X`) //! ```raw //! o--------------------------o @@ -55,36 +53,10 @@ //! / // / //! o X o //! ``` -//! If we want to keep the line along its skeleton with constant radius, the top right corner -//! would move further and further outward as we decrease the angle of the joint. Eventually it reaches infinity! -//! (i.e. not great to fix it up with discard in the fragment shader either) -//! -//! To prevent this we need to generate this shape: -//! ```raw -//! a-------------------b -//! \ -//! X=================X \ -//! // \ -//! c---------d // e -//! / // / -//! f X g -//! ``` +//! The problem is that the top right corner would move further and further outward as we decrease the angle of the joint. +//! Instead, we generate overlapping, detached quads and handle line joints as cut-outs in the fragment shader. //! -//! To achieve this we need to do one of: -//! 1) generating a new triangle at `[d,b,e]` -//! * can't do that without significant preprocessing, makes the entire pipeline much more complicated -//! 2) twist one of the quads, making both quads overlap in the area of `[d,b,e]` (doesn't add any new vertices) -//! * unless (!) we duplicate vertices at one of the quads, the twist would need to continue for the rest of the strip! -//! 3) make one quad stop before the joint by forming `[a,b,d,c]`, the other one taking over the joint by forming `[b,e,g,f]` -//! * implies breaking up the quads (point d would be part of one quad but not the other) -//! -//! (2) and (3) can be implemented relatively easy if we're using a triangle strip! -//! (2) can be implemented in theory with a triangle list, but means that any joint has ripple effects on the rest of the list. -//! -//! TODO(andreas): Implement (3). Right now we don't implement line caps at all. -//! -//! -//! Line start/end caps (arrows/rounded/etc.) +//! Line start/end caps (arrows/etc.) //! ----------------------------------------------- //! Yet another place where our triangle *strip* comes in handy is that we can take triangles from superfluous quads to form pointy arrows. //! Again, we keep all the geometry calculating logic in the vertex shader. @@ -97,7 +69,6 @@ //! (start cap triangle only) (start+end triangle) (end triangle only) //! //! -//! //! Things we might try in the future //! ---------------------------------- //! * more line properties From 3647e988ae0d9b7db4c8f7b946f4a87356ea0f50 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 12:08:28 +0100 Subject: [PATCH 07/14] boxes & rectangles use now line strips --- .../re_renderer/src/line_drawable_builder.rs | 110 ++++++++---------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/crates/viewer/re_renderer/src/line_drawable_builder.rs b/crates/viewer/re_renderer/src/line_drawable_builder.rs index 1310bbee48b7..6a790ab471ba 100644 --- a/crates/viewer/re_renderer/src/line_drawable_builder.rs +++ b/crates/viewer/re_renderer/src/line_drawable_builder.rs @@ -307,7 +307,6 @@ impl<'a, 'ctx> LineBatchBuilder<'a, 'ctx> { /// Add box outlines from a unit cube transformed by `transform`. /// - /// Internally adds 12 line segments with rounded line heads. /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good) #[inline] pub fn add_box_outline_from_transform( @@ -324,36 +323,15 @@ impl<'a, 'ctx> LineBatchBuilder<'a, 'ctx> { transform.transform_point3(glam::vec3(0.5, 0.5, -0.5)), transform.transform_point3(glam::vec3(0.5, 0.5, 0.5)), ]; - self.add_segments( - [ - // bottom: - (corners[0b000], corners[0b001]), - (corners[0b000], corners[0b010]), - (corners[0b011], corners[0b001]), - (corners[0b011], corners[0b010]), - // top: - (corners[0b100], corners[0b101]), - (corners[0b100], corners[0b110]), - (corners[0b111], corners[0b101]), - (corners[0b111], corners[0b110]), - // sides: - (corners[0b000], corners[0b100]), - (corners[0b001], corners[0b101]), - (corners[0b010], corners[0b110]), - (corners[0b011], corners[0b111]), - ] - .into_iter(), - ) - .flags(LineDrawableBuilder::default_box_flags()) + self.add_box_from_corners(corners) } /// Add box outlines. /// - /// Internally adds 12 line segments with rounded line heads. + /// Internally a single closed line strip. /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good) /// /// Returns None for empty and non-finite boxes. - #[inline] pub fn add_box_outline( &mut self, bbox: &re_math::BoundingBox, @@ -362,35 +340,54 @@ impl<'a, 'ctx> LineBatchBuilder<'a, 'ctx> { return None; } - let corners = bbox.corners(); - Some( - self.add_segments( - [ - // bottom: - (corners[0b000], corners[0b001]), - (corners[0b000], corners[0b010]), - (corners[0b011], corners[0b001]), - (corners[0b011], corners[0b010]), - // top: - (corners[0b100], corners[0b101]), - (corners[0b100], corners[0b110]), - (corners[0b111], corners[0b101]), - (corners[0b111], corners[0b110]), - // sides: - (corners[0b000], corners[0b100]), - (corners[0b001], corners[0b101]), - (corners[0b010], corners[0b110]), - (corners[0b011], corners[0b111]), - ] - .into_iter(), - ) - .flags(LineDrawableBuilder::default_box_flags()), + Some(self.add_box_from_corners(bbox.corners())) + } + + fn add_box_from_corners(&mut self, corners: [glam::Vec3; 8]) -> LineStripBuilder<'_, 'ctx> { + let mut strip_index = self.0.strips_buffer.len() as u32; + + // Bottom plus connection to top. + self.add_vertices( + [ + // bottom loop + corners[0b000], + corners[0b001], + corners[0b011], + corners[0b010], + corners[0b000], + // joined to top loop + corners[0b100], + corners[0b101], + corners[0b111], + corners[0b110], + corners[0b100], + ] + .into_iter(), + strip_index, ) + .ok_or_log_error_once(); + strip_index += 1; + + // remaining side edges. + for line in [ + [corners[0b001], corners[0b101]], + [corners[0b010], corners[0b110]], + [corners[0b011], corners[0b111]], + ] { + self.add_vertices(line.into_iter(), strip_index) + .ok_or_log_error_once(); + strip_index += 1; + } + + let num_strips_added = 4; + let num_vertices_added = 10 + 3 * 2; + self.create_strip_builder(num_strips_added, num_vertices_added) + .flags(LineDrawableBuilder::default_box_flags()) } /// Add rectangle outlines. /// - /// Internally adds 4 line segments with rounded line heads. + /// Internally adds a single linestrip with 5 vertices. /// Disables color gradient since we don't support gradients in this setup yet (i.e. enabling them does not look good) #[inline] pub fn add_rectangle_outline( @@ -399,18 +396,13 @@ impl<'a, 'ctx> LineBatchBuilder<'a, 'ctx> { extent_u: glam::Vec3, extent_v: glam::Vec3, ) -> LineStripBuilder<'_, 'ctx> { - self.add_segments( + self.add_strip( [ - (top_left_corner, top_left_corner + extent_u), - ( - top_left_corner + extent_u, - top_left_corner + extent_u + extent_v, - ), - ( - top_left_corner + extent_u + extent_v, - top_left_corner + extent_v, - ), - (top_left_corner + extent_v, top_left_corner), + top_left_corner, + top_left_corner + extent_u, + top_left_corner + extent_u + extent_v, + top_left_corner + extent_v, + top_left_corner, ] .into_iter(), ) From d2058199412e8e3afabe3b890ee0599eda0b8d62 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 14:12:26 +0100 Subject: [PATCH 08/14] disable alpha-to-coverage blending workaround if blending is disabled --- crates/viewer/re_renderer/shader/composite.wgsl | 9 ++++++++- crates/viewer/re_renderer/src/renderer/compositor.rs | 8 ++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/viewer/re_renderer/shader/composite.wgsl b/crates/viewer/re_renderer/shader/composite.wgsl index 74a56a60da02..53b4bd1ec692 100644 --- a/crates/viewer/re_renderer/shader/composite.wgsl +++ b/crates/viewer/re_renderer/shader/composite.wgsl @@ -7,6 +7,7 @@ struct CompositeUniformBuffer { outline_color_layer_a: vec4f, outline_color_layer_b: vec4f, outline_radius_pixel: f32, + blend_with_background: u32, }; @group(1) @binding(0) var uniforms: CompositeUniformBuffer; @@ -27,11 +28,17 @@ fn main(in: FragmentInput) -> @location(0) vec4f { // but are about the location of the texel in the target texture. var color = textureSample(color_texture, nearest_sampler, in.texcoord); + // TODO(andreas): We assume that the color from the texture does *not* have pre-multiplied alpha. // This is a brittle workaround for the alpha-to-coverage issue described in `ViewBuilder::MAIN_TARGET_ALPHA_TO_COVERAGE_COLOR_STATE`: // We need this because otherwise the feathered edges of alpha-to-coverage would be overly bright, as after // MSAA-resolve they end up with an unusually low alpha value relative to the color value. - color = vec4f(color.rgb * color.a, color.a); + if uniforms.blend_with_background == 0 { + // To not apply this hack needlessly and account for alpha from alpha to coverage, we have to ignore alpha values if blending is disabled. + color = vec4f(color.rgb, 1.0); + } else { + color = vec4f(color.rgb * color.a, color.a); + } // Outlines { diff --git a/crates/viewer/re_renderer/src/renderer/compositor.rs b/crates/viewer/re_renderer/src/renderer/compositor.rs index 2205a4052333..34b0e2691e94 100644 --- a/crates/viewer/re_renderer/src/renderer/compositor.rs +++ b/crates/viewer/re_renderer/src/renderer/compositor.rs @@ -24,7 +24,9 @@ mod gpu_data { pub struct CompositeUniformBuffer { pub outline_color_layer_a: wgpu_buffer_types::Vec4, pub outline_color_layer_b: wgpu_buffer_types::Vec4, - pub outline_radius_pixel: wgpu_buffer_types::F32RowPadded, + pub outline_radius_pixel: f32, + pub blend_with_background: u32, + pub padding: [u32; 2], pub end_padding: [wgpu_buffer_types::PaddingRow; 16 - 3], } } @@ -72,7 +74,9 @@ impl CompositorDrawData { gpu_data::CompositeUniformBuffer { outline_color_layer_a: outline_config.color_layer_a.into(), outline_color_layer_b: outline_config.color_layer_b.into(), - outline_radius_pixel: outline_config.outline_radius_pixel.into(), + outline_radius_pixel: outline_config.outline_radius_pixel, + blend_with_background: enable_blending as u32, + padding: Default::default(), end_padding: Default::default(), }, ); From 62c00233f81e1e6c16f44962415564a46fa4bbda Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 14:18:23 +0100 Subject: [PATCH 09/14] make closed line loops appear closed --- .../src/visualizers/geo_line_strings.rs | 12 +++++++++++- .../re_space_view_spatial/src/visualizers/lines2d.rs | 9 ++++++++- .../re_space_view_spatial/src/visualizers/lines3d.rs | 9 ++++++++- .../src/visualizers/utilities/proc_mesh_vis.rs | 11 +++++++++-- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs index fcdbe6b3aac8..9272b10fb873 100644 --- a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs +++ b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs @@ -1,5 +1,8 @@ use re_log_types::{EntityPath, Instance}; -use re_renderer::{renderer::LineDrawDataError, PickingLayerInstanceId}; +use re_renderer::{ + renderer::{LineDrawDataError, LineStripFlags}, + PickingLayerInstanceId, +}; use re_space_view::{DataResultQuery as _, RangeResultsExt as _}; use re_types::{ archetypes::GeoLineStrings, @@ -170,6 +173,13 @@ impl GeoLineStringsVisualizer { .copied() .unwrap_or(walkers::Position::from_lat_lon(0.0, 0.0)), )) + // Looped lines should be connected with rounded corners, so we always add outward extending caps. + .flags( + LineStripFlags::FLAG_CAP_START_ROUND + | LineStripFlags::FLAG_CAP_END_ROUND + | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS + | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, + ) .color(*color) .picking_instance_id(*instance) .outline_mask_ids( diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs index 0ce6a34f2ac0..7a5bef23a8d5 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs @@ -1,5 +1,5 @@ use re_log_types::Instance; -use re_renderer::{LineDrawableBuilder, PickingLayerInstanceId}; +use re_renderer::{renderer::LineStripFlags, LineDrawableBuilder, PickingLayerInstanceId}; use re_types::{ archetypes::LineStrips2D, components::{ClassId, Color, DrawOrder, KeypointId, LineStrip2D, Radius, ShowLabels, Text}, @@ -89,6 +89,13 @@ impl Lines2DVisualizer { .add_strip_2d(strip.iter().copied().map(Into::into)) .color(color) .radius(radius) + // Looped lines should be connected with rounded corners, so we always add outward extending caps. + .flags( + LineStripFlags::FLAG_CAP_START_ROUND + | LineStripFlags::FLAG_CAP_END_ROUND + | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS + | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, + ) .picking_instance_id(PickingLayerInstanceId(i as _)); if let Some(outline_mask_ids) = ent_context diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs index aecd3ec440e0..e42d0c7b4e38 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs @@ -1,5 +1,5 @@ use re_log_types::Instance; -use re_renderer::PickingLayerInstanceId; +use re_renderer::{renderer::LineStripFlags, PickingLayerInstanceId}; use re_types::{ archetypes::LineStrips3D, components::{ClassId, Color, KeypointId, LineStrip3D, Radius, ShowLabels, Text}, @@ -91,6 +91,13 @@ impl Lines3DVisualizer { { let lines = line_batch .add_strip(strip.iter().copied().map(Into::into)) + // Looped lines should be connected with rounded corners, so we always add outward extending caps. + .flags( + LineStripFlags::FLAG_CAP_START_ROUND + | LineStripFlags::FLAG_CAP_END_ROUND + | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS + | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, + ) .color(color) .radius(radius) .picking_instance_id(PickingLayerInstanceId(i as _)); diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs index 73a09081a2f3..ae80a1a5b177 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs @@ -1,6 +1,6 @@ use re_entity_db::InstancePathHash; use re_log_types::Instance; -use re_renderer::renderer::GpuMeshInstance; +use re_renderer::renderer::{GpuMeshInstance, LineStripFlags}; use re_renderer::{LineDrawableBuilder, PickingLayerInstanceId, RenderContext}; use re_types::components::{self, FillMode}; use re_viewer_context::{ @@ -195,7 +195,14 @@ where ) .color(color) .radius(radius) - .picking_instance_id(PickingLayerInstanceId(instance_index as _)); + .picking_instance_id(PickingLayerInstanceId(instance_index as _)) + // Looped lines should be connected with rounded corners. + .flags( + LineStripFlags::FLAG_CAP_START_ROUND + | LineStripFlags::FLAG_CAP_END_ROUND + | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS + | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, + ); if let Some(outline_mask_ids) = ent_context .highlight From 4a32166fc2cea7271716f5550cc1f86051f20061 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 14:33:37 +0100 Subject: [PATCH 10/14] make frustum outline consist of a few loops rather than individual segments, add alias for flag combination --- .../re_renderer/src/line_drawable_builder.rs | 5 +- .../viewer/re_renderer/src/renderer/lines.rs | 7 +++ .../src/visualizers/geo_line_strings.rs | 7 +-- .../src/visualizers/cameras.rs | 53 ++++++++++--------- .../src/visualizers/lines2d.rs | 7 +-- .../src/visualizers/lines3d.rs | 7 +-- .../visualizers/utilities/proc_mesh_vis.rs | 7 +-- 7 files changed, 41 insertions(+), 52 deletions(-) diff --git a/crates/viewer/re_renderer/src/line_drawable_builder.rs b/crates/viewer/re_renderer/src/line_drawable_builder.rs index 6a790ab471ba..73d9916beaed 100644 --- a/crates/viewer/re_renderer/src/line_drawable_builder.rs +++ b/crates/viewer/re_renderer/src/line_drawable_builder.rs @@ -89,10 +89,7 @@ impl<'ctx> LineDrawableBuilder<'ctx> { } pub fn default_box_flags() -> LineStripFlags { - LineStripFlags::FLAG_CAP_END_ROUND - | LineStripFlags::FLAG_CAP_START_ROUND - | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS - | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS + LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS } } diff --git a/crates/viewer/re_renderer/src/renderer/lines.rs b/crates/viewer/re_renderer/src/renderer/lines.rs index 5ff42e5efe30..e42cf6f5d6a9 100644 --- a/crates/viewer/re_renderer/src/renderer/lines.rs +++ b/crates/viewer/re_renderer/src/renderer/lines.rs @@ -236,6 +236,13 @@ bitflags! { /// /// TODO(andreas): Could be moved to per batch flags. const FLAG_FORCE_ORTHO_SPANNING = 0b1000_0000; + + /// Combination of flags to extend lines outwards with round caps. + const FLAGS_OUTWARD_EXTENDING_ROUND_CAPS = + LineStripFlags::FLAG_CAP_START_ROUND.bits() | + LineStripFlags::FLAG_CAP_END_ROUND.bits() | + LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS.bits() | + LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS.bits(); } } diff --git a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs index 9272b10fb873..31b34c4454b4 100644 --- a/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs +++ b/crates/viewer/re_space_view_map/src/visualizers/geo_line_strings.rs @@ -174,12 +174,7 @@ impl GeoLineStringsVisualizer { .unwrap_or(walkers::Position::from_lat_lon(0.0, 0.0)), )) // Looped lines should be connected with rounded corners, so we always add outward extending caps. - .flags( - LineStripFlags::FLAG_CAP_START_ROUND - | LineStripFlags::FLAG_CAP_END_ROUND - | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS - | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, - ) + .flags(LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS) .color(*color) .picking_instance_id(*instance) .outline_mask_ids( diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs index 5df5f2604146..603b04b586d0 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs @@ -124,25 +124,27 @@ impl CamerasVisualizer { let up_triangle = [ pinhole.unproject(vec3(0.4 * w, 0.0, z)), - pinhole.unproject(vec3(0.6 * w, 0.0, z)), pinhole.unproject(vec3(0.5 * w, -0.1 * w, z)), + pinhole.unproject(vec3(0.6 * w, 0.0, z)), ]; - let segments = [ - // Frustum corners: - (glam::Vec3::ZERO, corners[0]), - (glam::Vec3::ZERO, corners[1]), - (glam::Vec3::ZERO, corners[2]), - (glam::Vec3::ZERO, corners[3]), - // Rectangle around "far plane": - (corners[0], corners[1]), - (corners[1], corners[2]), - (corners[2], corners[3]), - (corners[3], corners[0]), + let strips = vec![ + // Frustum rectangle, connected with zero point. + vec![ + corners[0], + corners[1], + glam::Vec3::ZERO, + corners[2], + corners[3], + glam::Vec3::ZERO, + corners[0], + corners[3], + glam::Vec3::ZERO, + ], + // Missing piece of the rectangle at the far plane. + vec![corners[1], corners[2]], // Triangle indicating up direction: - (up_triangle[0], up_triangle[1]), - (up_triangle[1], up_triangle[2]), - (up_triangle[2], up_triangle[0]), + vec![up_triangle[0], up_triangle[1], up_triangle[2]], ]; let radius = re_renderer::Size::new_ui_points(1.0); @@ -160,15 +162,18 @@ impl CamerasVisualizer { ) .outline_mask_ids(entity_highlight.overall) .picking_object_id(instance_layer_id.object); - let lines = batch - .add_segments(segments.into_iter()) - .radius(radius) - .color(CAMERA_COLOR) - .flags(LineStripFlags::FLAG_CAP_END_ROUND | LineStripFlags::FLAG_CAP_START_ROUND) - .picking_instance_id(instance_layer_id.instance); - - if let Some(outline_mask_ids) = entity_highlight.instances.get(&instance) { - lines.outline_mask_ids(*outline_mask_ids); + + for strip in strips { + let lines = batch + .add_strip(strip.into_iter()) + .radius(radius) + .color(CAMERA_COLOR) + .flags(LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS) + .picking_instance_id(instance_layer_id.instance); + + if let Some(outline_mask_ids) = entity_highlight.instances.get(&instance) { + lines.outline_mask_ids(*outline_mask_ids); + } } // world_from_camera is the transform to the pinhole origin diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs index 7a5bef23a8d5..5a868ea4045d 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/lines2d.rs @@ -90,12 +90,7 @@ impl Lines2DVisualizer { .color(color) .radius(radius) // Looped lines should be connected with rounded corners, so we always add outward extending caps. - .flags( - LineStripFlags::FLAG_CAP_START_ROUND - | LineStripFlags::FLAG_CAP_END_ROUND - | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS - | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, - ) + .flags(LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS) .picking_instance_id(PickingLayerInstanceId(i as _)); if let Some(outline_mask_ids) = ent_context diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs index e42d0c7b4e38..754cf13ef3ba 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/lines3d.rs @@ -92,12 +92,7 @@ impl Lines3DVisualizer { let lines = line_batch .add_strip(strip.iter().copied().map(Into::into)) // Looped lines should be connected with rounded corners, so we always add outward extending caps. - .flags( - LineStripFlags::FLAG_CAP_START_ROUND - | LineStripFlags::FLAG_CAP_END_ROUND - | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS - | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, - ) + .flags(LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS) .color(color) .radius(radius) .picking_instance_id(PickingLayerInstanceId(i as _)); diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs index ae80a1a5b177..0f4a57402d3e 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/utilities/proc_mesh_vis.rs @@ -197,12 +197,7 @@ where .radius(radius) .picking_instance_id(PickingLayerInstanceId(instance_index as _)) // Looped lines should be connected with rounded corners. - .flags( - LineStripFlags::FLAG_CAP_START_ROUND - | LineStripFlags::FLAG_CAP_END_ROUND - | LineStripFlags::FLAG_CAP_START_EXTEND_OUTWARDS - | LineStripFlags::FLAG_CAP_END_EXTEND_OUTWARDS, - ); + .flags(LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS); if let Some(outline_mask_ids) = ent_context .highlight From 9ed19cf0c284670b40dabee99027ff54ae08b744 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 15:18:23 +0100 Subject: [PATCH 11/14] fix box/rect line builder reserve --- .../viewer/re_space_view_spatial/src/visualizers/boxes2d.rs | 6 +++--- .../viewer/re_space_view_spatial/src/visualizers/boxes3d.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs index 4012e90338bd..dff98f6d0962 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/boxes2d.rs @@ -226,9 +226,9 @@ impl VisualizerSystem for Boxes2DVisualizer { return Ok(()); } - // Each box consists of 4 independent lines of 2 vertices each. - line_builder.reserve_strips(num_boxes * 4)?; - line_builder.reserve_vertices(num_boxes * 4 * 2)?; + // Each box consists of one strip with a total of 5 vertices each. + line_builder.reserve_strips(num_boxes)?; + line_builder.reserve_vertices(num_boxes * 5)?; let timeline = ctx.query.timeline(); let all_half_sizes_indexed = iter_primitive_array::<2, f32>( diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs b/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs index 76646387a987..afb768b15844 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/boxes3d.rs @@ -169,9 +169,9 @@ impl VisualizerSystem for Boxes3DVisualizer { match fill_mode { FillMode::DenseWireframe | FillMode::MajorWireframe => { - // Each box consists of 12 independent lines with 2 vertices each. - builder.line_builder.reserve_strips(num_boxes * 12)?; - builder.line_builder.reserve_vertices(num_boxes * 12 * 2)?; + // Each box consists of 4 strips with a total of 16 vertices + builder.line_builder.reserve_strips(num_boxes * 4)?; + builder.line_builder.reserve_vertices(num_boxes * 16)?; } FillMode::Solid => { // No lines. From b0b01e6a6caa05b9002485c590fb412389514e26 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 15:18:41 +0100 Subject: [PATCH 12/14] make typos happy --- crates/viewer/re_renderer/shader/lines.wgsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/viewer/re_renderer/shader/lines.wgsl b/crates/viewer/re_renderer/shader/lines.wgsl index 4769ff887a69..00a3b6297387 100644 --- a/crates/viewer/re_renderer/shader/lines.wgsl +++ b/crates/viewer/re_renderer/shader/lines.wgsl @@ -311,10 +311,10 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { } fn distance_to_line_sq(pos: vec3f, line_a: vec3f, line_b: vec3f) -> f32 { - let pa = pos - line_a; - let ba = line_b - line_a; - let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - let to_line = pa - ba*h; + let a_to_pos = pos - line_a; + let a_to_b = line_b - line_a; + let h = clamp(dot(a_to_pos, a_to_b) / dot(a_to_b, a_to_b), 0.0, 1.0); + let to_line = a_to_pos - a_to_b * h; return dot(to_line, to_line); } From 1ce5c3d6acab8391c837860108a930e0c8207602 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 15:31:54 +0100 Subject: [PATCH 13/14] fix typo, use saturate over clamp --- crates/viewer/re_renderer/shader/lines.wgsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/viewer/re_renderer/shader/lines.wgsl b/crates/viewer/re_renderer/shader/lines.wgsl index 00a3b6297387..3f830e682d0d 100644 --- a/crates/viewer/re_renderer/shader/lines.wgsl +++ b/crates/viewer/re_renderer/shader/lines.wgsl @@ -266,7 +266,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { center_position += quad_dir * (size_boost * select(-1.0, 1.0, is_at_quad_end)); } - // Filtered list of flats that the fragment shader is interested in. + // Filtered list of flags that the fragment shader is interested in. var fragment_flags = strip_data.flags & FLAG_COLOR_GRADIENT; // If this is a triangle cap, we blow up our ("virtual") quad by a given factor. @@ -313,7 +313,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut { fn distance_to_line_sq(pos: vec3f, line_a: vec3f, line_b: vec3f) -> f32 { let a_to_pos = pos - line_a; let a_to_b = line_b - line_a; - let h = clamp(dot(a_to_pos, a_to_b) / dot(a_to_b, a_to_b), 0.0, 1.0); + let h = saturate(dot(a_to_pos, a_to_b) / dot(a_to_b, a_to_b)); let to_line = a_to_pos - a_to_b * h; return dot(to_line, to_line); } From 44463874bad48a69eb789cac5ba9b49a0132893d Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 11 Nov 2024 15:53:46 +0100 Subject: [PATCH 14/14] fix slight rendering artifact on camera frustum --- .../src/visualizers/cameras.rs | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs index 603b04b586d0..d42a29483489 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/cameras.rs @@ -130,21 +130,31 @@ impl CamerasVisualizer { let strips = vec![ // Frustum rectangle, connected with zero point. - vec![ - corners[0], - corners[1], - glam::Vec3::ZERO, - corners[2], - corners[3], - glam::Vec3::ZERO, - corners[0], - corners[3], - glam::Vec3::ZERO, - ], + ( + vec![ + corners[0], + corners[1], + glam::Vec3::ZERO, + corners[2], + corners[3], + glam::Vec3::ZERO, + corners[0], + corners[3], + glam::Vec3::ZERO, + ], + LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS, + ), // Missing piece of the rectangle at the far plane. - vec![corners[1], corners[2]], - // Triangle indicating up direction: - vec![up_triangle[0], up_triangle[1], up_triangle[2]], + ( + vec![corners[1], corners[2]], + LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS, + ), + // Triangle indicating up direction. + // Don't extend round caps here, this would reach into the frustum otherwise. + ( + vec![up_triangle[0], up_triangle[1], up_triangle[2]], + LineStripFlags::empty(), + ), ]; let radius = re_renderer::Size::new_ui_points(1.0); @@ -163,12 +173,12 @@ impl CamerasVisualizer { .outline_mask_ids(entity_highlight.overall) .picking_object_id(instance_layer_id.object); - for strip in strips { + for (strip, flags) in strips { let lines = batch .add_strip(strip.into_iter()) .radius(radius) .color(CAMERA_COLOR) - .flags(LineStripFlags::FLAGS_OUTWARD_EXTENDING_ROUND_CAPS) + .flags(flags) .picking_instance_id(instance_layer_id.instance); if let Some(outline_mask_ids) = entity_highlight.instances.get(&instance) {