Skip to content

SDF silhouettes are aliased — binary hit test, and DPR=1 on Retina #24

Description

@Haraldurk

Reported from a live session on a Retina MacBook Pro: SDF object edges read as rough / low-resolution. Visible as stair-stepping along the box and sphere silhouettes, with the shapes otherwise smoothly shaded.

Not investigated as a fix — this is a starting point for whoever picks it up, so the session does not begin by guessing.

Two causes, stacking

1. The silhouette has no partial coverage. src/inputs/SDFGenerator.js:572 decides the hit with a hard threshold:

if (d < 0.001) {

Coverage is therefore 1 or 0 per pixel. A ray that passes a hair outside the surface contributes nothing, and its neighbour contributes fully — which is a hard, stepped edge by construction, at any resolution.

2. The whole pipeline renders at DPR 1. src/main.js:236 sets renderer.setPixelRatio(1) deliberately: DPR 2 quadruples fill cost across 35+ shader passes for no visible gain on moving video. That reasoning holds for video and does not hold for a hard-edged synthetic silhouette, which is exactly where half linear resolution shows. On a Retina panel the SDF is rendered at half resolution and upscaled.

So the edge is both hard and magnified 2×.

What is likely the cheap fix

The raymarcher already tracks the quantity antialiasing needs. SDFGenerator.js:509–514:

float minD = 1e9;   // closest the ray ever came to the surface
...
minD = min(minD, d);

minD is currently used only for the glow halo (line 532). It is the ray's closest approach to the surface, which is the analytic ingredient for coverage: instead of a binary hit, derive alpha by comparing minD against the pixel's world-space footprint at that depth, and smoothstep it. That is a handful of lines in the fragment shader, costs no extra marching, and — unlike a resolution increase — is paid once in the SDF pass rather than across all 35+.

Worth checking whether the same treatment helps the reflection hit test at line 677 (if (rdd < 0.002)), which has the same binary shape.

Things that will NOT fix it, and why

  • antialias: true on the renderer (src/main.js:230 currently false). MSAA antialiases geometry edges. This silhouette is produced inside a fragment shader on a full-screen quad, so there is no geometry edge to sample — the setting would cost fill rate and change nothing.
  • Raising setPixelRatio globally. It would help the look and is the expensive answer: 4× fill across the entire chain, which the comment at that line rejects for good measured reasons. If supersampling turns out to be needed, it belongs on the SDF render target alone, not on the pipeline.

Files

  • src/inputs/SDFGenerator.js — hit test (572), minD (509–514), reflection hit (677)
  • src/main.js:236 — pixel ratio, and the reasoning to argue with before changing it

Verification note

Judge this on a still frame, at rest, on a Retina display. A moving SDF hides aliasing, and the environment the fix is developed in may not be the one that shows the bug — see .claude/skills/verify/SKILL.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions