-
Notifications
You must be signed in to change notification settings - Fork 453
Line strips are no longer a disconnected series of quads #8065
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
Changes from 10 commits
273fc3b
e85ed48
aa1b897
d842d6f
ba6dd0b
afab8b9
3647e98
d205819
62c0023
4a32166
9ed19cf
b0b01e6
1ce5c3d
4446387
9d6b7ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,9 @@ | |
const FLAG_COLOR_GRADIENT: u32 = 64u; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all the really important changes are in this file |
||
const FLAG_FORCE_ORTHO_SPANNING: u32 = 128u; | ||
|
||
// 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) | ||
// (see https://www.w3.org/TR/WGSL/#interpolation) | ||
|
@@ -60,14 +63,14 @@ | |
@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, | ||
|
@@ -166,6 +169,8 @@ | |
|
||
// 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: | ||
|
@@ -189,14 +194,14 @@ | |
|
||
// 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. | ||
|
@@ -226,19 +231,27 @@ | |
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. | ||
|
@@ -253,57 +266,78 @@ | |
center_position += quad_dir * (size_boost * select(-1.0, 1.0, is_at_quad_end)); | ||
} | ||
|
||
var active_radius = strip_radius; | ||
// 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)) { | ||
active_radius *= batch.triangle_cap_width_factor * triangle_cap_size_factor; | ||
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 | ||
let dir_up = normalize(cross(camera_ray.direction, quad_dir)); | ||
|
||
let round_cap_circle_center = center_position; | ||
|
||
var pos: vec3f; | ||
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, as well as round start/end caps. | ||
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; | ||
} | ||
|
||
// 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); | ||
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.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.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; | ||
|
||
return out; | ||
} | ||
|
||
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); | ||
Check warning on line 316 in crates/viewer/re_renderer/shader/lines.wgsl
|
||
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); | ||
|
||
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)); | ||
|
||
// 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; | ||
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! | ||
//return coverage + 0.1; | ||
return coverage; | ||
} | ||
|
||
|
@@ -317,9 +351,8 @@ | |
// 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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
improves alpha to coverage artifact suppression story. In other words this gets non-map views to where they were in 0.19
Tbh. I'm not 100% sure why I did get still artifacts for boxes in 3D without this, but it also didn't feel that surprising.