From e2ed42fd75dbf64e04e4b9f6802c38bb811fe59c Mon Sep 17 00:00:00 2001 From: ira Date: Thu, 17 Aug 2023 22:09:19 +0200 Subject: [PATCH] Fix gizmo lines deforming or disappearing when partially behind the camera (#9470) If a line has one point behind the camera(near plane) then it would deform or, if the `depth_bias` setting was set to a negative value, disappear. ## Solution The issue is that performing a perspective divide does not work correctly for points behind the near plane and a perspective divide is used inside the shader to define the line width in screen space. The solution is to perform near plane clipping manually inside the shader before the perspective divide is done. --- crates/bevy_gizmos/src/lines.wgsl | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/bevy_gizmos/src/lines.wgsl b/crates/bevy_gizmos/src/lines.wgsl index 6696f51e62..2d0c341dcb 100644 --- a/crates/bevy_gizmos/src/lines.wgsl +++ b/crates/bevy_gizmos/src/lines.wgsl @@ -43,8 +43,13 @@ fn vertex(vertex: VertexInput) -> VertexOutput { 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.); + var clip_a = view.view_proj * vec4(vertex.position_a, 1.); + var clip_b = view.view_proj * vec4(vertex.position_b, 1.); + + // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader. + clip_a = clip_near_plane(clip_a, clip_b); + clip_b = clip_near_plane(clip_b, clip_a); + let clip = mix(clip_a, clip_b, position.z); let resolution = view.viewport.zw; @@ -92,6 +97,18 @@ fn vertex(vertex: VertexInput) -> VertexOutput { return VertexOutput(clip_position, color); } +fn clip_near_plane(a: vec4, b: vec4) -> vec4 { + // Move a if a is behind the near plane and b is in front. + if a.z > a.w && b.z <= b.w { + // Interpolate a towards b until it's at the near plane. + let distance_a = a.z - a.w; + let distance_b = b.z - b.w; + let t = distance_a / (distance_a - distance_b); + return a + (b - a) * t; + } + return a; +} + struct FragmentInput { @location(0) color: vec4, };