Skip to content

Commit

Permalink
TileLayerBase: Clip tiles using depth
Browse files Browse the repository at this point in the history
This changes the WebGL tile renderer to render tiles zoomed-in first,
using the depth buffer as to clip lower layers of tiles without blending
any transparent tiles. Any tile that uses alpha is drawn last, with the
highest possible Z, to allow blending with the lower layer of already drawn
tiles.
  • Loading branch information
puckipedia committed Aug 8, 2023
1 parent e7b4e5b commit 463b5b5
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 64 deletions.
11 changes: 11 additions & 0 deletions src/ol/array.js
Expand Up @@ -49,6 +49,17 @@ export function ascending(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}

/**
* Compare function sorting arrays in descending order. Safe to use for numeric values.
* @param {*} a The first object to be compared.
* @param {*} b The second object to be compared.
* @return {number} A negative number, zero, or a positive number as the first
* argument is greater than, equal to, or less than the second.
*/
export function descending(a, b) {
return a < b ? 1 : a > b ? -1 : 0;
}

/**
* {@link module:ol/tilegrid/TileGrid~TileGrid#getZForResolution} can use a function
* of this type to determine which nearest resolution to use.
Expand Down
164 changes: 100 additions & 64 deletions src/ol/renderer/webgl/TileLayerBase.js
Expand Up @@ -8,7 +8,6 @@ import TileRange from '../../TileRange.js';
import TileState from '../../TileState.js';
import WebGLLayerRenderer from './Layer.js';
import {abstract, getUid} from '../../util.js';
import {ascending} from '../../array.js';
import {create as createMat4} from '../../vec/mat4.js';
import {
createOrUpdate as createTileCoord,
Expand All @@ -21,6 +20,7 @@ import {
scale as scaleTransform,
translate as translateTransform,
} from '../../transform.js';
import {descending} from '../../array.js';
import {fromUserExtent} from '../../proj.js';
import {getIntersection, isEmpty} from '../../extent.js';
import {toSize} from '../../size.js';
Expand Down Expand Up @@ -48,7 +48,7 @@ const empty = {};
* @return {number} A depth value.
*/
function depthForZ(z) {
return 2 * (1 - 1 / (z + 1)) - 1;
return 1 / (z + 2);
}

/**
Expand Down Expand Up @@ -409,7 +409,7 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
* @protected
*/
beforeTilesRender(frameState, tilesWithAlpha) {
this.helper.prepareDraw(this.frameState, !tilesWithAlpha);
this.helper.prepareDraw(this.frameState, !tilesWithAlpha, true);
}

/**
Expand Down Expand Up @@ -440,6 +440,79 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
alpha
) {}

drawTile_(
frameState,
tileRepresentation,
tileZ,
gutter,
extent,
alphaLookup,
tileGrid
) {
if (!tileRepresentation.loaded) {
return;
}
const tile = tileRepresentation.tile;
const tileCoord = tile.tileCoord;
const tileCoordKey = getTileCoordKey(tileCoord);
const alpha = tileCoordKey in alphaLookup ? alphaLookup[tileCoordKey] : 1;

const tileResolution = tileGrid.getResolution(tileZ);
const tileSize = toSize(tileGrid.getTileSize(tileZ), this.tempSize_);
const tileOrigin = tileGrid.getOrigin(tileZ);
const tileExtent = tileGrid.getTileCoordExtent(tileCoord);
// tiles with alpha are rendered last to allow blending
const depth = alpha < 1 ? -1 : depthForZ(tileZ);
if (alpha < 1) {
frameState.animate = true;
}

const viewState = frameState.viewState;
const centerX = viewState.center[0];
const centerY = viewState.center[1];

const tileWidthWithGutter = tileSize[0] + 2 * gutter;
const tileHeightWithGutter = tileSize[1] + 2 * gutter;

const aspectRatio = tileWidthWithGutter / tileHeightWithGutter;

const centerI = (centerX - tileOrigin[0]) / (tileSize[0] * tileResolution);
const centerJ = (tileOrigin[1] - centerY) / (tileSize[1] * tileResolution);

const tileScale = viewState.resolution / tileResolution;

const tileCenterI = tileCoord[1];
const tileCenterJ = tileCoord[2];

resetTransform(this.tileTransform_);
scaleTransform(
this.tileTransform_,
2 / ((frameState.size[0] * tileScale) / tileWidthWithGutter),
-2 / ((frameState.size[1] * tileScale) / tileWidthWithGutter)
);
rotateTransform(this.tileTransform_, viewState.rotation);
scaleTransform(this.tileTransform_, 1, 1 / aspectRatio);
translateTransform(
this.tileTransform_,
(tileSize[0] * (tileCenterI - centerI) - gutter) / tileWidthWithGutter,
(tileSize[1] * (tileCenterJ - centerJ) - gutter) / tileHeightWithGutter
);

this.renderTile(
/** @type {TileRepresentation} */ (tileRepresentation),
this.tileTransform_,
frameState,
extent,
tileResolution,
tileSize,
tileOrigin,
tileExtent,
depth,
gutter,
alpha
);
}

/**
* Render the layer.
* @param {import("../../Map.js").FrameState} frameState Frame state.
Expand Down Expand Up @@ -564,77 +637,40 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
this.beforeTilesRender(frameState, blend);

const representationsByZ = tileRepresentationLookup.representationsByZ;
const zs = Object.keys(representationsByZ).map(Number).sort(ascending);

const zs = Object.keys(representationsByZ).map(Number).sort(descending);
for (let j = 0, jj = zs.length; j < jj; ++j) {
const tileZ = zs[j];
for (const tileRepresentation of representationsByZ[tileZ]) {
if (!tileRepresentation.loaded) {
continue;
}
const tile = tileRepresentation.tile;
const tileCoord = tile.tileCoord;
const tileCoord = tileRepresentation.tile.tileCoord;
const tileCoordKey = getTileCoordKey(tileCoord);
const alpha =
tileCoordKey in alphaLookup ? alphaLookup[tileCoordKey] : 1;

if (alpha < 1) {
frameState.animate = true;
if (tileCoordKey in alphaLookup) {
continue;
}

const tileResolution = tileGrid.getResolution(tileZ);
const tileSize = toSize(tileGrid.getTileSize(tileZ), this.tempSize_);
const tileOrigin = tileGrid.getOrigin(tileZ);
const tileExtent = tileGrid.getTileCoordExtent(tileCoord);
const depth = depthForZ(tileZ);

const viewState = frameState.viewState;
const centerX = viewState.center[0];
const centerY = viewState.center[1];

const tileWidthWithGutter = tileSize[0] + 2 * gutter;
const tileHeightWithGutter = tileSize[1] + 2 * gutter;

const aspectRatio = tileWidthWithGutter / tileHeightWithGutter;

const centerI =
(centerX - tileOrigin[0]) / (tileSize[0] * tileResolution);
const centerJ =
(tileOrigin[1] - centerY) / (tileSize[1] * tileResolution);

const tileScale = viewState.resolution / tileResolution;

const tileCenterI = tileCoord[1];
const tileCenterJ = tileCoord[2];

resetTransform(this.tileTransform_);
scaleTransform(
this.tileTransform_,
2 / ((frameState.size[0] * tileScale) / tileWidthWithGutter),
-2 / ((frameState.size[1] * tileScale) / tileWidthWithGutter)
);
rotateTransform(this.tileTransform_, viewState.rotation);
scaleTransform(this.tileTransform_, 1, 1 / aspectRatio);
translateTransform(
this.tileTransform_,
(tileSize[0] * (tileCenterI - centerI) - gutter) /
tileWidthWithGutter,
(tileSize[1] * (tileCenterJ - centerJ) - gutter) /
tileHeightWithGutter
this.drawTile_(
frameState,
tileRepresentation,
tileZ,
gutter,
extent,
alphaLookup,
tileGrid
);
}
}

this.renderTile(
/** @type {TileRepresentation} */ (tileRepresentation),
this.tileTransform_,
for (const tileRepresentation of representationsByZ[z]) {
const tileCoord = tileRepresentation.tile.tileCoord;
const tileCoordKey = getTileCoordKey(tileCoord);
if (tileCoordKey in alphaLookup) {
this.drawTile_(
frameState,
extent,
tileResolution,
tileSize,
tileOrigin,
tileExtent,
depth,
tileRepresentation,
z,
gutter,
alpha
extent,
alphaLookup,
tileGrid
);
}
}
Expand Down

0 comments on commit 463b5b5

Please sign in to comment.