Replace square mosaic with corridor elongated in travel direction
The camera moves primarily in +X (drift = uTime * uSpeed * 0.45) with a slight +Z component (drift * 0.7). A square 9x9 mosaic wastes half its tiles behind the camera. A corridor shape with 13 tiles along travel (X) and 5 tiles perpendicular (Z) uses 65 tiles to cover the same useful area — and refetches happen only when the camera approaches the forward edge, not all four sides. - GROUND_CORRIDOR_X = 13 (≈13s of forward travel at uSpeed=1) - GROUND_CORRIDOR_Z = 5 - GROUND_BUFFER_X = 2, GROUND_BUFFER_Z = 1 - Shader uniform uGroundSize is now vec2 (sizeX, sizeZ) - UV mapping: gUv = (hitPoint - centre) / size + 0.5 with per-axis size
This commit is contained in:
1 parent
e80a270b05
commit
47f2c16eba
1 file changed
+35
-21
Vendored
+35
-21
@@ -55,7 +55,7 @@ var FRAG_SRC = [
|
||||
'uniform float uTime;',
|
||||
'uniform sampler2D uGround;',
|
||||
'uniform vec2 uGroundCenter;', // current mosaic centre (world units)
|
||||
'uniform float uGroundSize;', // mosaic side length (world units)
|
||||
'uniform vec2 uGroundSize;', // mosaic side extents in X and Z (world units)
|
||||
'uniform vec3 uSky;',
|
||||
'uniform vec3 uCloud;',
|
||||
'uniform vec3 uCloudShadow;',
|
||||
@@ -270,8 +270,13 @@ function init(canvas) {
|
||||
var GROUND_TILE_URL = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/';
|
||||
var GROUND_TILE_Z = 11;
|
||||
var GROUND_TILE_SIZE = 256;
|
||||
var GROUND_MOSAIC_SIZE = 9; // 9x9 tiles around the camera (~81 tiles, fast initial load)
|
||||
var GROUND_BUFFER_TILES = 2; // refetch when this close to edge
|
||||
// Camera travels at uTime * uSpeed * 0.45 in +X (and +X*0.7 in +Z).
|
||||
// A corridor elongated in X gives more forward coverage per tile.
|
||||
// 13 tiles along X (≈13s of travel) x 5 tiles wide in Z = 65 tiles.
|
||||
var GROUND_CORRIDOR_X = 13; // tiles along travel direction
|
||||
var GROUND_CORRIDOR_Z = 5; // tiles perpendicular
|
||||
var GROUND_BUFFER_X = 2; // refetch when this close to forward edge
|
||||
var GROUND_BUFFER_Z = 1; // refetch when this close to side edge
|
||||
// Passau tile at zoom 11
|
||||
var GROUND_BASE_X = 1099;
|
||||
var GROUND_BASE_Y = 706;
|
||||
@@ -296,22 +301,25 @@ function fetchTileImage(tileX, tileY) {
|
||||
});
|
||||
}
|
||||
|
||||
function drawTileAt(tileX, tileY, centerTile) {
|
||||
var r = Math.floor(GROUND_MOSAIC_SIZE / 2);
|
||||
var dx = (tileX - centerTile.x + r) * GROUND_TILE_SIZE;
|
||||
var dy = (tileY - centerTile.y + r) * GROUND_TILE_SIZE;
|
||||
if (dx < 0 || dx >= groundCanvas.width || dy < 0 || dy >= groundCanvas.height) {
|
||||
function drawTileAt(dx, dz, centerTileX, centerTileY) {
|
||||
var tileX = centerTileX + dx;
|
||||
var tileY = centerTileY + dz;
|
||||
var halfX = Math.floor(GROUND_CORRIDOR_X / 2);
|
||||
var halfZ = Math.floor(GROUND_CORRIDOR_Z / 2);
|
||||
var canvasX = (dx + halfX) * GROUND_TILE_SIZE;
|
||||
var canvasY = (dz + halfZ) * GROUND_TILE_SIZE;
|
||||
if (canvasX < 0 || canvasX >= groundCanvas.width || canvasY < 0 || canvasY >= groundCanvas.height) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return fetchTileImage(tileX, tileY).then(function(img) {
|
||||
groundCtx.drawImage(img, dx, dy);
|
||||
groundCtx.drawImage(img, canvasX, canvasY);
|
||||
groundLoadedTiles[tileX + '_' + tileY] = true;
|
||||
return true;
|
||||
}).catch(function() {
|
||||
// On failure draw a neutral gray so the gap is at least
|
||||
// not the bright fog colour
|
||||
groundCtx.fillStyle = '#3a4458';
|
||||
groundCtx.fillRect(dx, dy, GROUND_TILE_SIZE, GROUND_TILE_SIZE);
|
||||
groundCtx.fillRect(canvasX, canvasY, GROUND_TILE_SIZE, GROUND_TILE_SIZE);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -320,14 +328,15 @@ function drawTileAt(tileX, tileY, centerTile) {
|
||||
// and loads every tile in the new range.
|
||||
function rebuildGroundMosaic(centerTileX, centerTileY) {
|
||||
groundCenterTile = { x: centerTileX, y: centerTileY };
|
||||
var r = Math.floor(GROUND_MOSAIC_SIZE / 2);
|
||||
var halfX = Math.floor(GROUND_CORRIDOR_X / 2);
|
||||
var halfZ = Math.floor(GROUND_CORRIDOR_Z / 2);
|
||||
// Clear with fog colour so the texture is never undefined
|
||||
groundCtx.fillStyle = '#7c9aa1';
|
||||
groundCtx.fillRect(0, 0, groundCanvas.width, groundCanvas.height);
|
||||
var promises = [];
|
||||
for (var x = centerTileX - r; x <= centerTileX + r; x++) {
|
||||
for (var y = centerTileY - r; y <= centerTileY + r; y++) {
|
||||
promises.push(drawTileAt(x, y, groundCenterTile));
|
||||
for (var dx = -halfX; dx <= halfX; dx++) {
|
||||
for (var dz = -halfZ; dz <= halfZ; dz++) {
|
||||
promises.push(drawTileAt(dx, dz, centerTileX, centerTileY));
|
||||
}
|
||||
}
|
||||
return Promise.all(promises).then(function() {
|
||||
@@ -343,10 +352,15 @@ function rebuildGroundMosaic(centerTileX, centerTileY) {
|
||||
function maybeRefetchGround(cameraWorldX, cameraWorldZ, gl) {
|
||||
var cameraTileX = GROUND_BASE_X + Math.floor(cameraWorldX);
|
||||
var cameraTileY = GROUND_BASE_Y + Math.floor(cameraWorldZ);
|
||||
var r = Math.floor(GROUND_MOSAIC_SIZE / 2);
|
||||
var dx = Math.abs(cameraTileX - groundCenterTile.x);
|
||||
var dy = Math.abs(cameraTileY - groundCenterTile.y);
|
||||
if (dx > r - GROUND_BUFFER_TILES || dy > r - GROUND_BUFFER_TILES) {
|
||||
var halfX = Math.floor(GROUND_CORRIDOR_X / 2);
|
||||
var halfZ = Math.floor(GROUND_CORRIDOR_Z / 2);
|
||||
var dx = cameraTileX - groundCenterTile.x;
|
||||
var dy = cameraTileY - groundCenterTile.y;
|
||||
// Only refetch when camera approaches the FRONT of the corridor
|
||||
// (the only direction the camera moves toward). Side and back
|
||||
// buffers keep refetches minimal.
|
||||
if (dx > halfX - GROUND_BUFFER_X || dx < -halfX + GROUND_BUFFER_X ||
|
||||
dy > halfZ - GROUND_BUFFER_Z || dy < -halfZ + GROUND_BUFFER_Z) {
|
||||
if (groundUpdatePending) return;
|
||||
groundUpdatePending = true;
|
||||
rebuildGroundMosaic(cameraTileX, cameraTileY).then(function() {
|
||||
@@ -361,8 +375,8 @@ function initGroundSystem(glCtx) {
|
||||
if (groundCanvas) return; // already initialised
|
||||
groundGl = glCtx;
|
||||
groundCanvas = document.createElement('canvas');
|
||||
groundCanvas.width = GROUND_TILE_SIZE * GROUND_MOSAIC_SIZE;
|
||||
groundCanvas.height = GROUND_TILE_SIZE * GROUND_MOSAIC_SIZE;
|
||||
groundCanvas.width = GROUND_TILE_SIZE * GROUND_CORRIDOR_X;
|
||||
groundCanvas.height = GROUND_TILE_SIZE * GROUND_CORRIDOR_Z;
|
||||
groundCtx = groundCanvas.getContext('2d');
|
||||
// Pre-fill with fog colour so partially-loaded mosaic still
|
||||
// shows the right base colour
|
||||
@@ -487,7 +501,7 @@ window.clouds3dRender = function(target) {
|
||||
y: groundCenterTile.y - GROUND_BASE_Y
|
||||
};
|
||||
gl.uniform2f(s.uGroundCenter, centerWorld.x, centerWorld.y);
|
||||
gl.uniform1f(s.uGroundSize, GROUND_MOSAIC_SIZE);
|
||||
gl.uniform2f(s.uGroundSize, GROUND_CORRIDOR_X, GROUND_CORRIDOR_Z);
|
||||
|
||||
var drift = now * speed * 0.45;
|
||||
if (performance.now() - groundLastCheck > 1500) {
|
||||
|
||||
Reference in new issue
Block a user