# Objective Adopt code from [bevy_polyline](https://github.com/ForesightMiningSoftwareCorporation/bevy_polyline) for gizmo line-rendering. This adds configurable width and perspective rendering for the lines. Many thanks to @mtsr for the initial work on bevy_polyline. Thanks to @aevyrie for maintaining it, @nicopap for adding the depth_bias feature and the other [contributors](https://github.com/ForesightMiningSoftwareCorporation/bevy_polyline/graphs/contributors) for squashing bugs and keeping bevy_polyline up-to-date. #### Before  #### After - with line perspective  Line perspective is not on by default because with perspective there is no default line width that works for every scene. <details><summary>After - without line perspective</summary> <p>  </p> </details> Somewhat unexpectedly, the performance is improved with this PR. At 200,000 lines in many_gizmos I get ~110 FPS on main and ~200 FPS with this PR. I'm guessing this is a CPU side difference as I would expect the rendering technique to be more expensive on the GPU to some extent, but I am not entirely sure. --------- Co-authored-by: Jonas Matser <github@jonasmatser.nl> Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Nicola Papale <nico@nicopap.ch> Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
106 lines
3.1 KiB
WebGPU Shading Language
106 lines
3.1 KiB
WebGPU Shading Language
#ifdef GIZMO_3D
|
|
#import bevy_pbr::mesh_view_bindings
|
|
#else
|
|
#import bevy_sprite::mesh2d_view_bindings
|
|
#endif
|
|
|
|
struct LineGizmoUniform {
|
|
line_width: f32,
|
|
depth_bias: f32,
|
|
#ifdef SIXTEEN_BYTE_ALIGNMENT
|
|
// WebGL2 structs must be 16 byte aligned.
|
|
_padding: vec2<f32>,
|
|
#endif
|
|
}
|
|
|
|
@group(1) @binding(0)
|
|
var<uniform> line_gizmo: LineGizmoUniform;
|
|
|
|
struct VertexInput {
|
|
@location(0) position_a: vec3<f32>,
|
|
@location(1) position_b: vec3<f32>,
|
|
@location(2) color_a: vec4<f32>,
|
|
@location(3) color_b: vec4<f32>,
|
|
@builtin(vertex_index) index: u32,
|
|
};
|
|
|
|
struct VertexOutput {
|
|
@builtin(position) clip_position: vec4<f32>,
|
|
@location(0) color: vec4<f32>,
|
|
};
|
|
|
|
@vertex
|
|
fn vertex(vertex: VertexInput) -> VertexOutput {
|
|
var positions = array<vec3<f32>, 6>(
|
|
vec3(0., -0.5, 0.),
|
|
vec3(0., -0.5, 1.),
|
|
vec3(0., 0.5, 1.),
|
|
vec3(0., -0.5, 0.),
|
|
vec3(0., 0.5, 1.),
|
|
vec3(0., 0.5, 0.)
|
|
);
|
|
let position = positions[vertex.index];
|
|
|
|
// algorithm based on https://wwwtyro.net/2019/11/18/instanced-lines.html
|
|
let clip_a = view.view_proj * vec4(vertex.position_a, 1.);
|
|
let clip_b = view.view_proj * vec4(vertex.position_b, 1.);
|
|
let clip = mix(clip_a, clip_b, position.z);
|
|
|
|
let resolution = view.viewport.zw;
|
|
let screen_a = resolution * (0.5 * clip_a.xy / clip_a.w + 0.5);
|
|
let screen_b = resolution * (0.5 * clip_b.xy / clip_b.w + 0.5);
|
|
|
|
let x_basis = normalize(screen_a - screen_b);
|
|
let y_basis = vec2(-x_basis.y, x_basis.x);
|
|
|
|
var color = mix(vertex.color_a, vertex.color_b, position.z);
|
|
|
|
var line_width = line_gizmo.line_width;
|
|
var alpha = 1.;
|
|
|
|
#ifdef PERSPECTIVE
|
|
line_width /= clip.w;
|
|
#endif
|
|
|
|
// Line thinness fade from https://acegikmo.com/shapes/docs/#anti-aliasing
|
|
if line_width < 1. {
|
|
color.a *= line_width;
|
|
line_width = 1.;
|
|
}
|
|
|
|
let offset = line_width * (position.x * x_basis + position.y * y_basis);
|
|
let screen = mix(screen_a, screen_b, position.z) + offset;
|
|
|
|
var depth: f32;
|
|
if line_gizmo.depth_bias >= 0. {
|
|
depth = clip.z * (1. - line_gizmo.depth_bias);
|
|
} else {
|
|
let epsilon = 4.88e-04;
|
|
// depth * (clip.w / depth)^-depth_bias. So that when -depth_bias is 1.0, this is equal to clip.w
|
|
// and when equal to 0.0, it is exactly equal to depth.
|
|
// the epsilon is here to prevent the depth from exceeding clip.w when -depth_bias = 1.0
|
|
// clip.w represents the near plane in homogenous clip space in bevy, having a depth
|
|
// of this value means nothing can be in front of this
|
|
// The reason this uses an exponential function is that it makes it much easier for the
|
|
// user to chose a value that is convinient for them
|
|
depth = clip.z * exp2(-line_gizmo.depth_bias * log2(clip.w / clip.z - epsilon));
|
|
}
|
|
|
|
var clip_position = vec4(clip.w * ((2. * screen) / resolution - 1.), depth, clip.w);
|
|
|
|
return VertexOutput(clip_position, color);
|
|
}
|
|
|
|
struct FragmentInput {
|
|
@location(0) color: vec4<f32>,
|
|
};
|
|
|
|
struct FragmentOutput {
|
|
@location(0) color: vec4<f32>,
|
|
};
|
|
|
|
@fragment
|
|
fn fragment(in: FragmentInput) -> FragmentOutput {
|
|
return FragmentOutput(in.color);
|
|
}
|