Soften UV edges + smaller refetch shifts

Two visual issues reported: stripes from sides at tilts of view, and
tiles 'hopping' one tile per refetch.

Stripes: caused by gUv.y going OOB at sideways rays where hitPoint.x
is large; CLAMP_TO_EDGE returned the same edge colour across a wide
band, looking like a stripe. Added shader edgeFade that fades OOB
samples smoothly to uBg instead.

Hopping: each refetch shifts centerTile by many tiles (depends on
when in the cycle it fires). With BUFFER_LONG = 1 (refetch at
projLong > halfLong - 1 = 11), the centerTile only ever shifts by
about 1 tile per refetch. Refetches happen more often (~ every
8 sec instead of ~ every 96 sec) but each is tiny.

reverted partial-refetch optimisation: causes ghosting because old
tiles stay at old canvas positions but uGroundCenter moves, so
the shader samples stale content. Better to clear canvas and rely
on edgeFade to hide the brief fog state.
This commit is contained in:
Richard Zink committed 2026-10-07 21:17:45 +02:00
1 parent af5cda467f
commit a3f5bf6084
1 file changed
+11 -5
+11 -5
View File
@@ -206,9 +206,15 @@ var FRAG_SRC = [
'vec2 offset = vec2(hitPoint.x, hitPoint.z) - uGroundCenter;',
'vec2 rotated = vec2(offset.x * 0.0 + offset.y * 1.0, -offset.x * 1.0 + offset.y * 0.0);',
'vec2 gUv = rotated / uGroundSize + 0.5;',
// Soft fade at UV edges so OOB samples blend to fog instead of producing a hard stripe.
'float perpFade = smoothstep(-0.08, 0.05, gUv.y) * smoothstep(1.08, 0.95, gUv.y);',
'float longFade = smoothstep(-0.08, 0.05, gUv.x) * smoothstep(1.08, 0.95, gUv.x);',
'float edgeFade = perpFade * longFade;',
'vec3 groundCol = texture2D(uGround, gUv).rgb;',
'float fogAmount = clamp(tGround / 14.0, 0.0, 1.0);',
'groundCol = mix(groundCol, uBg, fogAmount);',
// Edge fade: ground smoothly disappears near canvas boundary.
'groundCol = mix(uBg, groundCol, edgeFade);',
// Ground mask: full ground when looking down, none when up.
'float gmask = (tGround > 0.0) ? smoothstep(0.0, -0.10, rd.y) : 0.0;',
'col = mix(col, groundCol, gmask);',
@@ -277,7 +283,7 @@ var GROUND_TRAVEL_COS = 0.0; // cos(90 deg)
var GROUND_TRAVEL_SIN = 1.0; // sin(90 deg)
var GROUND_CORRIDOR_LONG = 25; // tiles along travel direction (~3.5 min of flight at uSpeed=1)
var GROUND_CORRIDOR_PERP = 5; // tiles perpendicular to travel
var GROUND_BUFFER_LONG = 3; // refetch when this close to forward edge
var GROUND_BUFFER_LONG = 1; // refetch when this close to forward edge
var GROUND_BUFFER_PERP = 1; // refetch when this close to side edge
// Passau tile at zoom 11
var GROUND_BASE_X = 1099;
@@ -336,13 +342,13 @@ function rebuildGroundMosaic(centerTileX, centerTileY) {
groundCenterTile = { x: centerTileX, y: centerTileY };
var halfLong = Math.floor(GROUND_CORRIDOR_LONG / 2);
var halfPerp = Math.floor(GROUND_CORRIDOR_PERP / 2);
// Clear with fog colour so the texture is never undefined
// Clear with fog colour so we don't get ghosting from old tiles
// at wrong canvas positions. The shader's edgeFade makes OOB
// samples blend smoothly to uBg, so the brief fog state during
// tile fetch is invisible.
groundCtx.fillStyle = '#7c9aa1';
groundCtx.fillRect(0, 0, groundCanvas.width, groundCanvas.height);
var promises = [];
// Iterate the corridor in its own rotated frame (long, perp),
// then map each (long, perp) to a world tile via rotation and to
// a canvas pixel via the iteration indices.
for (var perp = -halfPerp; perp <= halfPerp; perp++) {
for (var lng = -halfLong; lng <= halfLong; lng++) {
// Tile offset from camera in world (tile) coords, rotated