Skip to content

Stop the section plane deleting the half of the model it is meant to show - #138

Draft
kmatzen wants to merge 1 commit into
mainfrom
worktree-clip-grazing-fix
Draft

Stop the section plane deleting the half of the model it is meant to show#138
kmatzen wants to merge 1 commit into
mainfrom
worktree-clip-grazing-fix

Conversation

@kmatzen

@kmatzen kmatzen commented Aug 3, 2026

Copy link
Copy Markdown
Owner

What you saw

Turning on the clipping plane (the scissors tool) and orbiting to a grazing view made the scene render wrong — and orbiting further, to look at the kept half from below the plane, made it disappear entirely.

Measured on the reported project, looking up at the kept half from 60° below the plane: the viewport drew 0.00% of the frame where the equivalent cut solid covers 32.24%.

elevation section plane (before) ground truth
−60 0.00% 32.24%
−40 5.93% 28.09%
−20 12.32% 19.63%
−6 8.70% 10.77%
+20 20.04% 20.32%

Views from the cut side (positive elevations) were already correct, which is why this reads as "it breaks when I rotate".

Two faults

The plane crossing was solved by an unguarded division. The marcher tested every sample against the plane and, on the cut-away side, jumped to it via (u_clipPos - ro.a) / rd.a. Grazing views drive rd.a to zero and the quotient to an infinity; a camera sitting on the plane makes it 0/0. Since NaN > 0.0 is false, those rays fell into a fallback that advanced by the hit threshold — roughly half a pixel — and the clipped branch never checked whether the ray had left the bounding box, so it could never terminate. Such a ray burned all 1024 iterations creeping, and rays with real surface still ahead of them ran out of budget before reaching it.

Ending the march at the crossing loses surfaces just short of it. Solving the crossing in closed form exposed this. Over-relaxed sphere tracing (Keinert et al., already used here for #76) deliberately overshoots and corrects on the following iteration, so the step that finds a surface routinely lands past it first. Cutting the ray off the moment it passes the plane discards that pending correction. A surface just short of the plane is exactly the common case, since the plane gets positioned against the feature being inspected — so the model vanished wholesale rather than in patches.

The plane now bounds which hits count, not how far the march may travel. The ray is abandoned only from a step where Keinert's condition has just certified that nothing was skipped, which is also what keeps a hit on the cut-away side from being accepted.

Also fixed

The cross-section was shaded by testing whether a marched hit landed within three hit-thresholds of the plane — proximity rather than causation, so a genuine face lying flat against the plane was painted as cut face across its whole extent. Whether a ray starts on the cut face is already known exactly from its own entry point.

Removing the per-step clip branch also removed the budget-burning crawl: the probe run that produced these numbers went from 51s to 20s.

Test

e2e/clip-plane.spec.ts states the requirement without a reference image: intersecting the tree with a half-space of ordinary CSG geometry produces exactly the solid the plane should reveal, so rendering both and comparing silhouettes needs no golden file and survives the GPU and driver differences that sank golden images in #52 — both sides are rendered by the same marcher on the same machine, and only their agreement is asserted. Both clip directions are covered.

Fails on the old marcher (0.00% vs 32.24%); passes here with every angle agreeing to within 0.25 percentage points.

Verification

  • New spec passes with the fix, fails at HEAD
  • vitest run — 503 passed, 3 skipped
  • Golden images (SwiftShader) — 3 passed
  • viewport-frames.spec.ts — 10/10 in isolation

Note: this machine flakes on the full e2e suite under load regardless of this change — HEAD alone produces the same viewport-frames failures under --workers=1, and every one of them passes when run serially in isolation.

🤖 Generated with Claude Code

…show

Enabling the clipping plane and orbiting to look at the kept side edge-on,
or from behind it, thinned the model out and then dropped it entirely. On
the reported project, looking up at the kept half from 60 degrees below the
plane, the viewport drew nothing at all where the equivalent cut solid
covers a third of the frame.

Two faults, both in how the marcher handled the plane.

It tested every sample against the plane and, on the cut-away side, jumped
to the plane by dividing by the ray's component along the clip axis. That
division has no guard: a view grazing the plane drives the component to
zero and the quotient to an infinity, and a camera sitting on the plane
makes it 0/0. NaN > 0.0 is false, so those rays fell through to a fallback
that advanced by the hit threshold — about half a pixel — and the clipped
branch never checked whether the ray had left the bounding box, so it could
not terminate. Such a ray spent all 1024 of its iterations creeping, and
rays with real surface still ahead of them ran out of budget before
reaching it.

Solving the crossing in closed form instead then exposed the second fault.
Ending the march at the crossing is wrong, because over-relaxed sphere
tracing deliberately overshoots and corrects on the next iteration: the
step that finds a surface routinely lands past it first. Cutting the ray
off the moment it passes the plane throws that pending correction away, so
a surface just short of the plane is lost — and that is the usual case,
since the plane gets placed against the feature being inspected. The plane
now bounds which hits count rather than how far the march may travel, and
the ray is abandoned only from a step where Keinert's condition has just
certified that nothing was skipped.

Also drops the cross-section shading test, which painted any marched hit
landing within three hit-thresholds of the plane as cut face. That measured
proximity rather than causation, so a real face lying flat against the
plane was tinted across its whole extent. Whether a ray starts on the cut
face is already known exactly from its own entry point.

The test states the requirement without a reference image: intersecting the
tree with a half-space of ordinary geometry produces exactly the solid the
plane should reveal, so rendering both and comparing silhouettes needs no
golden file and survives the GPU differences that sank golden images in
 #52. It fails on the old marcher at 0.00% against 32.24%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🚀 Preview deployed: https://worktree-clip-grazing-fix.sinter.pages.dev

(updates on every push to this PR)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant