fix: detail must yield to the crater carve (terrain-water task 10)

Batch 10_detail_edge measured 532 new below-sea pixels on seed
1158286446 and 44 on 72563200 -- exported terrain under the sea scalar
that the water grid (classify-driven, and correctly identical) calls dry.
Located: every one of them strictly inside the crater's physical carve
radius, 201-323 px out of 640.

Mechanism, confirmed against the data: detail moves a column's PRE-carve
height, and the carve is Lerp(curvedH, target, t). At t ~ 0.45-0.62 a
pre-carve drop of up to 8.4 m (relief plus the band-shift the warp
implies) still passes 0.02-3.53 m through, which is enough to push a
column sitting 0-3.45 m above the sea inside the bowl under it.

The crater carve is supposed to be the FINAL authority on its own
terrain, so detail now yields to it: CraterDetailWeight is 0 inside
0.80 x CraterRadius (exactly the carve's own radius) and feathers to
full by 1.05 x, scaling both the edge shift and the relief skin. Inside
the carve, B is bit-identical to A by construction, so the count is zero
rather than small. The reverted incision pass carried the same exclusion
for the same reason -- the principle outlived the pass that motivated it.

Re-running the 6 B_detail generations; A_off and the continuity run are
untouched by a detail-only change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stewart Howe 2026-08-08 21:23:28 -04:00
parent af1a6d912b
commit 59f49ac8a2
2 changed files with 34 additions and 5 deletions

View file

@ -52,6 +52,30 @@ public static class TerrainDetailPass
// ordering follows from it.
public const float EDGE_SAFETY_FRACTION = 2f / 3f;
// The crater carve is the final authority on its own terrain. Detail is masked
// out inside the physical carve radius (0.80 × CraterRadius — exactly where the
// carve applies) and feathers to full by 1.05 ×. Without this, detail moves a
// column's PRE-carve height, the carve's Lerp passes a fraction of that through,
// and columns sitting a metre or two above the sea inside the bowl get pushed
// under it — 532 px on seed 1158286446 in the first batch, terrain below the sea
// scalar that the (classify-driven, and correctly unchanged) water grid calls dry.
public const float CRATER_DETAIL_EXCL_FACTOR = 0.80f;
public const float CRATER_DETAIL_FEATHER_FACTOR = 1.05f;
/// <summary>
/// Detail weight from distance to the impact centre: 0 inside the carve, 1 well
/// outside it, linear between. <paramref name="craterRadius"/> is the configured
/// CraterRadius (the carve itself uses 0.80 × of it).
/// </summary>
public static float CraterDetailWeight(float distToCrater, float craterRadius)
{
float excl = craterRadius * CRATER_DETAIL_EXCL_FACTOR;
if (distToCrater <= excl) return 0f;
float feather = craterRadius * CRATER_DETAIL_FEATHER_FACTOR;
if (distToCrater >= feather) return 1f;
return (distToCrater - excl) / (feather - excl);
}
/// <summary>
/// The largest per-column knot shift this preset allows: bounded by the band
/// squeeze above, which also keeps the knot set strictly ordered