GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
fix: overlay wires pass at equal depth — the camera-facing lattice vanished
The wire pipeline shared the fill's strict LESS compare, relying on the
depth bias to win the coplanar tie. On camera-facing facets the slope term
is ~zero and the -2 constant bias dips below depth precision, so exactly
the wires NEAREST the camera z-fought the fill and lost — a hole in the
lattice that tracks the view, while the surviving limb wires read as
counter-moving: the overlay-mode 'sphere rotates with the camera'
illusion, diagnosed by the user as front-wire occlusion. The wire twin now
uses LESS_OR_EQUAL with depth writes off (an overlay adds no occlusion)
plus a larger constant bias for margin.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/vk/scene.rs | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/src/vk/scene.rs b/src/vk/scene.rs
index 078246b..42b8700 100644
--- a/src/vk/scene.rs
+++ b/src/vk/scene.rs
@@ -290,16 +290,25 @@ impl SceneStage {
// The wireframe twin: identical but rasterized as lines. Culling
// stays on so the wire view matches the fill's visible surface.
// A small negative depth bias pulls the lines toward the viewer,
- // so a wire pass drawn over its own filled mesh (the overlay
- // mode) wins the LESS depth test instead of z-fighting the
- // coplanar fill.
+ // and the compare is LESS_OR_EQUAL with writes off: a line
+ // fragment over its own fill interpolates the SAME plane, and on
+ // camera-facing facets (near-zero depth slope) the biased
+ // difference dips below depth precision — under strict LESS the
+ // front-center lattice z-fought the coplanar fill and LOST, wires
+ // surviving only toward the limb, which read as the mesh
+ // counter-rotating during orbits (the overlay-mode "sphere turns
+ // with the camera" illusion).
+ let depth_stencil_lines = vk::PipelineDepthStencilStateCreateInfo::default()
+ .depth_test_enable(true)
+ .depth_write_enable(false)
+ .depth_compare_op(vk::CompareOp::LESS_OR_EQUAL);
let wireframe_pipeline = wireframe_supported.then(|| {
let rasterization_lines = vk::PipelineRasterizationStateCreateInfo::default()
.polygon_mode(vk::PolygonMode::LINE)
.cull_mode(vk::CullModeFlags::BACK)
.front_face(vk::FrontFace::COUNTER_CLOCKWISE)
.depth_bias_enable(true)
- .depth_bias_constant_factor(-2.0)
+ .depth_bias_constant_factor(-4.0)
.depth_bias_slope_factor(-1.0)
.line_width(1.0);
device
@@ -312,7 +321,7 @@ impl SceneStage {
.viewport_state(&viewport_state)
.rasterization_state(&rasterization_lines)
.multisample_state(&multisample)
- .depth_stencil_state(&depth_stencil)
+ .depth_stencil_state(&depth_stencil_lines)
.color_blend_state(&color_blend)
.dynamic_state(&dynamic_state)
.layout(pipeline_layout)