perf(wire): collapse sub-pixel dash patterns to solid line
When the smallest dash / gap element of a linetype pattern would project below one pixel on screen, the gaps either shimmer as aliasing or vanish entirely — the user reads the line as solid anyway. Compute the min non-zero element of `pat0 / pat1` once in the vertex shader, flat- interpolate it across the instance, and let the fragment stage skip the arc-length dash test (and the `discard` for gap fragments) when `min_elem >= u.world_per_pixel` fails. Mirrors the existing hatch-LOD branch in `hatch.wgsl`; primary win is visual quality at extreme zoom-out, with a small fragment-shader op saving as a side effect. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
0637dde93c
commit
14a981dc87
1 changed files with 35 additions and 8 deletions
|
|
@ -44,6 +44,12 @@ struct VertexOut {
|
||||||
@location(2) pattern_length: f32,
|
@location(2) pattern_length: f32,
|
||||||
@location(3) pat0: vec4<f32>,
|
@location(3) pat0: vec4<f32>,
|
||||||
@location(4) pat1: vec4<f32>,
|
@location(4) pat1: vec4<f32>,
|
||||||
|
// World length of the smallest non-zero dash / gap element of this
|
||||||
|
// instance. Flat-interpolated (constant per instance) so the
|
||||||
|
// fragment stage can short-circuit the dash test when every gap
|
||||||
|
// projects below one pixel on screen. See the LOD branch in
|
||||||
|
// `fs_main`.
|
||||||
|
@location(5) @interpolate(flat) min_elem: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex fn vs_main(@builtin(vertex_index) vid: u32, in: InstanceIn) -> VertexOut {
|
@vertex fn vs_main(@builtin(vertex_index) vid: u32, in: InstanceIn) -> VertexOut {
|
||||||
|
|
@ -90,6 +96,19 @@ struct VertexOut {
|
||||||
let ndc_offset = perp_ndc * hw * side;
|
let ndc_offset = perp_ndc * hw * side;
|
||||||
let final_clip = clip_pos + vec4<f32>(ndc_offset * clip_pos.w, 0.0, 0.0);
|
let final_clip = clip_pos + vec4<f32>(ndc_offset * clip_pos.w, 0.0, 0.0);
|
||||||
|
|
||||||
|
// Smallest non-zero dash / gap element, in world units. Used by
|
||||||
|
// the fragment stage to decide when the pattern's finest feature
|
||||||
|
// would render below one pixel and should collapse to a solid line.
|
||||||
|
var min_elem: f32 = in.pattern_length;
|
||||||
|
let elems = array<f32, 8>(
|
||||||
|
in.pat0.x, in.pat0.y, in.pat0.z, in.pat0.w,
|
||||||
|
in.pat1.x, in.pat1.y, in.pat1.z, in.pat1.w,
|
||||||
|
);
|
||||||
|
for (var i = 0u; i < 8u; i++) {
|
||||||
|
let e = abs(elems[i]);
|
||||||
|
if e > 0.0 && e < min_elem { min_elem = e; }
|
||||||
|
}
|
||||||
|
|
||||||
var out: VertexOut;
|
var out: VertexOut;
|
||||||
out.clip_pos = final_clip;
|
out.clip_pos = final_clip;
|
||||||
out.color = in.color;
|
out.color = in.color;
|
||||||
|
|
@ -97,6 +116,7 @@ struct VertexOut {
|
||||||
out.pattern_length = in.pattern_length;
|
out.pattern_length = in.pattern_length;
|
||||||
out.pat0 = in.pat0;
|
out.pat0 = in.pat0;
|
||||||
out.pat1 = in.pat1;
|
out.pat1 = in.pat1;
|
||||||
|
out.min_elem = min_elem;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,9 +137,16 @@ fn in_dash(dist: f32, pat_len: f32, p0: vec4<f32>, p1: vec4<f32>) -> bool {
|
||||||
|
|
||||||
@fragment fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
|
@fragment fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
|
||||||
if in.pattern_length > 0.0 {
|
if in.pattern_length > 0.0 {
|
||||||
|
// LOD: once the pattern's smallest feature drops below ~1 px
|
||||||
|
// on screen, dash gaps alias / shimmer (or vanish completely)
|
||||||
|
// and the user reads the line as solid anyway. Skip the dash
|
||||||
|
// test and return solid colour — also saves the per-fragment
|
||||||
|
// arc-length math + `discard`.
|
||||||
|
if in.min_elem >= u.world_per_pixel {
|
||||||
if !in_dash(in.distance, in.pattern_length, in.pat0, in.pat1) {
|
if !in_dash(in.distance, in.pattern_length, in.pat0, in.pat1) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return in.color;
|
return in.color;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue