Skip to content

feat(render): recover crisp playable surface boundaries - #43

Merged
rroojrooj merged 12 commits into
mainfrom
codex/sp01-p2a-recovery
Jul 28, 2026
Merged

rroojrooj merged 12 commits into
mainfrom
codex/sp01-p2a-recovery

Conversation

@rroojrooj

Copy link
Copy Markdown
Owner

Outcome

Recover and correct the reviewed P2a surface-readability work on the current SP-00 base.

Changes

  • correct the Chambers authored-green proof camera
  • add independent raw RGB surface ownership and crisp green/fairway/bunker edges
  • restore bounded green collar and class-map/OSM precedence
  • make class-map smoothing fallback observable and tested
  • add the dedicated five-pose sp01-motion stability suite
  • document the SP-04 HD/far-photo seam handoff

Exact evidence

  • reviewed base: 88c67d6
  • candidate: 7e18723
  • independent acceptance: ACCEPT at 99%; zero Critical/High/Medium findings
  • focused motion config tests: 57/57
  • full suite: 382/382
  • git diff --check: pass
  • RTX 3060/WebGL 2 three-course before/after + comparison + synthetic smoke: clean
  • adjacent-pose stability: five clean frames at yaw -1/-0.5/0/+0.5/+1; no visible shimmer/pop/halo flash
  • GPU median: 15.400 ms -> 15.438 ms (+0.038 ms, within +3 ms budget)
  • textures: 57 -> 57; geometries: 463 -> 463

Scope notes

  • The broad Chambers high-survey HD/far-photo seam remains visible and is routed to SP-04.
  • One non-blocking Low remains: the pre-existing multi-material path may revisit an idempotently disposed shared Three texture.
  • This improves near/play surface readability; it is not a TrackMan/GSPro parity claim.

rroojrooj and others added 12 commits July 28, 2026 15:46
Task 1 (green step) of the P2a crisp-surfaces plan. The green->tan boundary was
the SUM of three soft layers (soft splat base, ~1.8m collar dilation, low-freq
aerial tint); sharpening any one didn't help (Task-0 gate finding). Fix: composite
the surface base colour IN-SHADER, gated by a crisp fwidth mask, overriding all
three AT the boundary.

turf.js (cache key v32 -> v33):
- uMaskRaw (RAW, unblurred mask) + uPalGreenA (linear palette green) uniforms.
- gCrisp = smoothstep(0.5-fwidth(gRaw), 0.5+fwidth(gRaw), gRaw) — collapses the
  0.45 m/px mask ramp to a ~1px mow line at the polygon edge.
- baseCol = mix(diffuseColor, uPalGreenA, gCrisp): crisp green base override.
- gEdge = gCrisp so the v29 checker/contour/sheen ride the crisp edge (they still
  apply INSIDE where gCrisp==1 — verified they survive).
- gVic (~4m green-vicinity dilation) suppresses the low-freq aerial tint near
  greens so the blurred green oval can't smear a soft halo across the crisp line.
- collar dilation tightened 1.8m -> 0.6m (gBlur now feeds only fr, not the
  character); a proper offset fescue collar is Task 2.

scene.js:
- _paintMask gains a blurPx param; a RAW (blurPx 0) packed mask is built and wired
  through _turfInputs (base + HD-patch share it) alongside the linear palette.
- green splat blur 1.0 -> 0.35 so the splat doesn't bleed green past the polygon
  where gCrisp==0 falls back to it.

fixtures: green_close repointed 138,-289 (a TEE — the green-look there is aerial
photo, not an OSM green, so gCrisp is correctly 0) -> 193,-263 (a real OSM green).

Verified before/after on the real green (193,-263): v32 soft ~2m airbrush -> v33
crisp mow line; v29 checker/contour intact; P1b tan fescue unregressed. Non-green
fragments byte-unchanged (gCrisp/gVic == 0 away from greens). 301/301 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fairway/rough step of Task 1. The fairway's soft cue is the STRIPE FADE (the
splat fairway->rough colour is already near-crisp at ~0.5m and both sides are
close tans/olives). So:
- mCrisp = fwidth-AA on the RAW mown mask (.r) — one shared raw-mask sample now
  fetches both .r (mown) and .g (green).
- the mow-stripe gate keys off max(mCrisp, cls.r) instead of the soft `m`, so
  stripes stop crisply at the OSM mow line (NDVI coverage cls.r preserved; its
  feather is reconciled in Task 3).

Deliberately did NOT override the fairway BASE colour: forcing the dry-olive
fairwayA over the whole mown gate recolours the fairway and reads near-black on
shaded slopes (verified regression) — that's scope creep past "crisp edges". The
green keeps its full base override; the fairway gets the crisp stripe edge only.

Verified on Chambers: fairway bright + striped (no darkening), stripes terminate
on the mow line; green edge + P1b tan unregressed. 301/301 tests green (v33).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…band

Bunker step of Task 1. The blurred OSM bunker mask left a soft desaturated
"sand halo" band around each bunker where the sand->grass gate ramped. Crisp it:
- bCrisp = smoothstep(0.5-fwidth(bRaw), 0.5+fwidth(bRaw), bRaw) on the OSM bunker
  mask, so the sand gate bm = max(bCrisp, cls.b*(1-m)) — a ~1px sand->grass line.
- The NDVI-detected sand (cls.b) union is unchanged; its feather is reconciled in
  Task 3.

Verified on Chambers: the near/play bunkers read with crisp scalloped edges, the
soft halo band is gone; grass/green/fairway unregressed. 301/301 tests green (v33).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the placeholder fr (a faint 16% darkening) with a distinct collar/fringe:
a ~0.8m apron ring just OUTSIDE the crisp putting-surface edge, coloured a touch
lighter + warmer than the green, with a crisp INNER mow line and a soft OUTER edge
into the rough, distance-faded (play/mid feature — far photo owns past ~60m).

turf.js (cache key v33 -> v34):
- collarBand = clamp(gDil,0,1) * (1-gCrisp) * gDistFade, where gDil is an 8-tap
  dilation of the RAW green mask at 0.8m (soft outer edge); (1-gCrisp) = crisp inner.
- 3-way base-colour stack: rough(splat) -> collarCol apron -> uPalGreenA putting
  surface. Compositing in baseCol means the collar gets the same grain/warm-mix/
  sun-rake/desat as everything else — reads as real mown grass, not a decal.
- collarCol = uPalGreenA * vec3(1.25,1.15,0.90) (tunable apron knobs); no new palette
  plumbing (derived in-shader from the existing green uniform).
- Removed the old blurred gBlur 8-tap block + fr; stripes now suppressed on the
  collar via collarBand (was fr); dropped the fr darkening line. Net cleanup.

Approximate collar (mask dilation), per the Task-0 gate's sanctioned fallback — no
per-green/whole-course SDF. All terrain meshes share the material so the collar
reaches the ring outside every green.

Verified live on Chambers (links), Sawgrass (parkland, green-on-green), St Andrews
(rough-OSM, 88 greens): a distinct lighter-green apron ring, crisp inner + soft
outer, no sticker read at mid, no egregious artifact; v29 checker/contour + Task-1
crisp edge + P1b tan unregressed. Non-green fragments byte-unchanged (collarBand==0
away from greens). 301/301 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dge (v35)

The crisp OSM edges (Task 1) stacked on top of the FEATHERED NDVI classmap union
(cls.r mown, cls.b sand, ~feathered coverage) produced a DOUBLE edge: a crisp OSM
line + a soft NDVI halo on the SAME boundary — most visible as a pale desaturated
"sand halo" ringing every OSM bunker.

Fix (turf.js, cache key v34 -> v35):
- osmNear = a 4-tap MAX dilation of the RAW OSM mask (mown .r OR bunker .b) over ~5m
  — a per-fragment "OSM authored a surface nearby" signal.
- In macroPre, before the mown union: cls.r *= 1-osmNear; cls.b *= 1-osmNear. Where
  OSM already authored the boundary the crisp OSM edge OWNS it (NDVI feather removed);
  NDVI survives only in genuine OSM gaps (osmNear==0) — its coverage role is preserved.

Verified on Chambers (bunker sand halo GONE, sand->grass edges now clean/crisp;
fairway unchanged — its NDVI mown was redundant with OSM; overview clean, no coverage
holes/rings) + Sawgrass (bunkers clean, green complex + water healthy). No
over-suppression. 301/301 tests. (P0a dot-screen + macro seam are still Task 4.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… + threshold (v36)

The runtime NDVI classmap is per-pixel scatter (false positives NDVI misreads as
mown/sand); unioned raw into the turf it rendered as a "dot-screen" checker/halftone
stipple across the ground. Confirmed by a live classmap→black swap (stipple vanished).

Fix (no data regen):
- scene.js: blur the classmap into SMOOTH coverage at LOAD (a 4px canvas blur in the
  loader callback) — isolated noise averages toward 0, contiguous real regions stay high.
- turf.js (cache key v35 -> v36): threshold the sampled classmap
  `cls.r/b = smoothstep(0.45, 0.75, ...)` so only CONFIDENT coverage adds surface; the
  blurred low-amplitude noise is dropped. (Task-3 osmNear suppression kept.)

Verified on Chambers: the near/mid dot-screen is GONE (matches a classmap-off
reference); fairways/greens/stripes intact (stripes are OSM-driven; only redundant/noisy
NDVI was removed). 301/301 tests. NB the HD-patch rectangular seam is a SEPARATE issue
(persists with the classmap off) — next commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rroojrooj
rroojrooj merged commit 03a1ff7 into main Jul 28, 2026
1 check passed
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