From 8a9fbd39562ceefb0675232d8e40c049539f3606 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Wed, 7 Oct 2026 21:31:33 +0200 Subject: [PATCH] Rotate corridor axes: long axis on canvas Y (vertical) Previous mapping placed the corridor's long axis (flight direction +Z) on canvas X (horizontal). User observed this as '90deg to flight direction' because the tiles appeared to scroll sideways on screen instead of along depth. Swap so the long axis is on canvas Y (vertical on canvas): - drawTileAt: canvasX = (perp + halfPerp + 0.5) * TILE_SIZE, canvasY = (lng + halfLong + 0.5) * TILE_SIZE - shader gUv: gUv.x = rotated.y / uGroundSize.x + 0.5 (perp axis), gUv.y = rotated.x / uGroundSize.y + 0.5 (long axis) - uGroundSize call: (PERP, LONG) instead of (LONG, PERP) Now the corridor's long axis runs vertically on canvas. The screen mapping (uv.y -> forward distance) reads the long axis as a vertical strip, matching user expectation that 'forward' tiles scroll top- to-bottom (or bottom-to-top) as the camera flies. --- vendor/clouds3d.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index 04f15aa..3952bf0 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -204,11 +204,15 @@ var FRAG_SRC = [ 'vec3 hitPoint = ro + rd * tGround;', // Project hitPoint onto the corridor rotated axes (travel ~35 deg). Then divide by per-axis extent so the corridor maps to UV 0..1. The mosaic canvas is drawn in this rotated frame, so the satellite image comes out oriented correctly. 'vec2 offset = vec2(hitPoint.x, hitPoint.z) - uGroundCenter;', + // rotated.x = world Z (corridor long axis, mapped to canvas Y), + // rotated.y = -world X (corridor perp, mapped to canvas X). '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;', + // Swap components so canvas X = perp direction, canvas Y = long direction + 'vec2 gUv = vec2(rotated.y / uGroundSize.x + 0.5, rotated.x / uGroundSize.y + 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);', + // After swapping gUv axes, perpFade operates on gUv.x and longFade on gUv.y + 'float perpFade = smoothstep(-0.08, 0.05, gUv.x) * smoothstep(1.08, 0.95, gUv.x);', + 'float longFade = smoothstep(-0.08, 0.05, gUv.y) * smoothstep(1.08, 0.95, gUv.y);', 'float edgeFade = perpFade * longFade;', 'vec3 groundCol = texture2D(uGround, gUv).rgb;', 'float fogAmount = clamp(tGround / 14.0, 0.0, 1.0);', @@ -315,14 +319,12 @@ function drawTileAt(lng, perp, dx, dz, centerTileX, centerTileY) { var halfLong = Math.floor(GROUND_CORRIDOR_LONG / 2); var halfPerp = Math.floor(GROUND_CORRIDOR_PERP / 2); // Canvas position: place each tile so its CENTRE sits at the - // rotated-frame coordinate (lng, perp). The shader maps - // gUv = rotated / uGroundSize + 0.5, so rotated = 0 is at the - // canvas centre (canvas_width/2, canvas_height/2). Iterating - // (lng, perp) over [-halfLong..halfLong] means tile centres at - // halfLong + 0.5 etc. Without the +0.5 the canvas would be - // half a tile off from the shader's expectation. - var canvasX = (lng + halfLong + 0.5) * GROUND_TILE_SIZE; - var canvasY = (perp + halfPerp + 0.5) * GROUND_TILE_SIZE; + // rotated-frame coordinate (lng, perp), but with lng along canvas Y + // (vertical) and perp along canvas X (horizontal). This way the + // corridor's long axis (flight direction +Z) appears as a vertical + // strip on canvas, matching the way depth maps on screen. + var canvasX = (perp + halfPerp + 0.5) * GROUND_TILE_SIZE; + var canvasY = (lng + halfLong + 0.5) * GROUND_TILE_SIZE; if (canvasX < 0 || canvasX >= groundCanvas.width || canvasY < 0 || canvasY >= groundCanvas.height) { return Promise.resolve(false); } @@ -529,7 +531,7 @@ window.clouds3dRender = function(target) { y: groundCenterTile.y - GROUND_BASE_Y }; gl.uniform2f(s.uGroundCenter, centerWorld.x, centerWorld.y); - gl.uniform2f(s.uGroundSize, GROUND_CORRIDOR_LONG, GROUND_CORRIDOR_PERP); + gl.uniform2f(s.uGroundSize, GROUND_CORRIDOR_PERP, GROUND_CORRIDOR_LONG); var drift = now * speed * 0.12; if (performance.now() - groundLastCheck > 1500) {