Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGL / Implement tile masking for vector tiles #15127

Merged
merged 5 commits into from Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 1 addition & 12 deletions examples/webgl-vector-tiles.js
Expand Up @@ -23,18 +23,7 @@ class WebGLVectorTileLayer extends VectorTile {
createRenderer() {
return new WebGLVectorTileLayerRenderer(this, {
style: {
fill: {
fragment: result.builder.getFillFragmentShader(),
vertex: result.builder.getFillVertexShader(),
},
stroke: {
fragment: result.builder.getStrokeFragmentShader(),
vertex: result.builder.getStrokeVertexShader(),
},
symbol: {
fragment: result.builder.getSymbolFragmentShader(),
vertex: result.builder.getSymbolVertexShader(),
},
builder: result.builder,
attributes: {
fillColor: {
size: 2,
Expand Down
45 changes: 14 additions & 31 deletions src/ol/render/webgl/VectorStyleRenderer.js
Expand Up @@ -75,9 +75,7 @@ export const Attributes = {

/**
* @typedef {Object} StyleShaders
* @property {ShaderProgram} [fill] Shaders for filling polygons.
* @property {ShaderProgram} [stroke] Shaders for line strings and polygon strokes.
* @property {ShaderProgram} [symbol] Shaders for symbols.
* @property {import("../../webgl/ShaderBuilder.js").ShaderBuilder} builder Shader builder with the appropriate presets.
* @property {AttributeDefinitions} [attributes] Custom attributes made available in the vertex shaders.
* Default shaders rely on the attributes in {@link Attributes}.
* @property {UniformDefinitions} [uniforms] Additional uniforms usable in shaders.
Expand All @@ -95,7 +93,8 @@ export const Attributes = {
* A layer renderer will typically maintain several of these in order to have several styles rendered separately.
*
* A VectorStyleRenderer instance can be created either from a literal style or from shaders using either
* `VectorStyleRenderer.fromStyle` or `VectorStyleRenderer.fromShaders`.
* `VectorStyleRenderer.fromStyle` or `VectorStyleRenderer.fromShaders`. The shaders should not be provided explicitly
* but instead as a preconfigured ShaderBuilder instance.
*
* The `generateBuffers` method returns a promise resolving to WebGL buffers that are intended to be rendered by the
* same renderer.
Expand All @@ -111,31 +110,15 @@ class VectorStyleRenderer {

this.hitDetectionEnabled_ = enableHitDetection;
let shaders = /** @type {StyleShaders} */ (styleOrShaders);

// TODO: improve discrimination between shaders and style
const isShaders =
'fill' in styleOrShaders ||
'stroke' in styleOrShaders ||
('symbol' in styleOrShaders && 'vertex' in styleOrShaders.symbol);
const isShaders = 'builder' in styleOrShaders;
if (!isShaders) {
const parseResult = parseLiteralStyle(
/** @type {import('../../style/literal.js').LiteralStyle} */ (
styleOrShaders
)
);
shaders = {
fill: {
vertex: parseResult.builder.getFillVertexShader(),
fragment: parseResult.builder.getFillFragmentShader(),
},
stroke: {
vertex: parseResult.builder.getStrokeVertexShader(),
fragment: parseResult.builder.getStrokeFragmentShader(),
},
symbol: {
vertex: parseResult.builder.getSymbolVertexShader(),
fragment: parseResult.builder.getSymbolFragmentShader(),
},
builder: parseResult.builder,
attributes: parseResult.attributes,
uniforms: parseResult.uniforms,
};
Expand All @@ -145,10 +128,10 @@ class VectorStyleRenderer {
* @type {boolean}
* @private
*/
this.hasFill_ = !!shaders.fill?.vertex;
this.hasFill_ = !!shaders.builder.getFillVertexShader();
if (this.hasFill_) {
this.fillVertexShader_ = shaders.fill.vertex;
this.fillFragmentShader_ = shaders.fill.fragment;
this.fillVertexShader_ = shaders.builder.getFillVertexShader();
this.fillFragmentShader_ = shaders.builder.getFillFragmentShader();
this.fillProgram_ = this.helper_.getProgram(
this.fillFragmentShader_,
this.fillVertexShader_
Expand All @@ -159,10 +142,10 @@ class VectorStyleRenderer {
* @type {boolean}
* @private
*/
this.hasStroke_ = !!shaders.stroke?.vertex;
this.hasStroke_ = !!shaders.builder.getStrokeVertexShader();
if (this.hasStroke_) {
this.strokeVertexShader_ = shaders.stroke && shaders.stroke.vertex;
this.strokeFragmentShader_ = shaders.stroke && shaders.stroke.fragment;
this.strokeVertexShader_ = shaders.builder.getStrokeVertexShader();
this.strokeFragmentShader_ = shaders.builder.getStrokeFragmentShader();
this.strokeProgram_ = this.helper_.getProgram(
this.strokeFragmentShader_,
this.strokeVertexShader_
Expand All @@ -173,10 +156,10 @@ class VectorStyleRenderer {
* @type {boolean}
* @private
*/
this.hasSymbol_ = !!shaders.symbol?.vertex;
this.hasSymbol_ = !!shaders.builder.getSymbolVertexShader();
if (this.hasSymbol_) {
this.symbolVertexShader_ = shaders.symbol && shaders.symbol.vertex;
this.symbolFragmentShader_ = shaders.symbol && shaders.symbol.fragment;
this.symbolVertexShader_ = shaders.builder.getSymbolVertexShader();
this.symbolFragmentShader_ = shaders.builder.getSymbolFragmentShader();
this.symbolProgram_ = this.helper_.getProgram(
this.symbolFragmentShader_,
this.symbolVertexShader_
Expand Down
55 changes: 49 additions & 6 deletions src/ol/renderer/webgl/TileLayerBase.js
Expand Up @@ -43,7 +43,8 @@ export const Uniforms = {
const empty = {};

/**
* Transform a zoom level into a depth value ranging from -1 to 1.
* Transform a zoom level into a depth value; zoom level zero has a depth value of 0.5, and increasing values
* have a depth trending towards 0
* @param {number} z A zoom level.
* @return {number} A depth value.
*/
Expand Down Expand Up @@ -412,6 +413,15 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
this.helper.prepareDraw(this.frameState, !tilesWithAlpha, true);
}

/**
* @param {import("../../Map.js").FrameState} frameState Frame state.
* @return {boolean} If returns false, tile mask rendering will be skipped
* @protected
*/
beforeTilesMaskRender(frameState) {
return false;
}

/**
* @param {TileRepresentation} tileRepresentation Tile representation
* @param {import("../../transform.js").Transform} tileTransform Tile transform
Expand Down Expand Up @@ -440,6 +450,15 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
alpha
) {}

/**
* @param {TileRepresentation} tileRepresentation Tile representation
* @param {number} tileZ Tile Z
* @param {import("../../extent.js").Extent} extent Render extent
* @param {number} depth Depth
* @protected
*/
renderTileMask(tileRepresentation, tileZ, extent, depth) {}

drawTile_(
frameState,
tileRepresentation,
Expand All @@ -449,7 +468,7 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
alphaLookup,
tileGrid
) {
if (!tileRepresentation.loaded) {
if (!tileRepresentation.ready) {
return;
}
const tile = tileRepresentation.tile;
Expand Down Expand Up @@ -593,7 +612,7 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
}
const tileCoord = tile.tileCoord;

if (tileRepresentation.loaded) {
if (tileRepresentation.ready) {
const alpha = tile.getAlpha(uid, time);
if (alpha === 1) {
// no need to look for alt tiles
Expand Down Expand Up @@ -634,10 +653,34 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
}
}

this.beforeTilesRender(frameState, blend);

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

const renderTileMask = this.beforeTilesMaskRender(frameState);

if (renderTileMask) {
for (let j = 0, jj = zs.length; j < jj; ++j) {
const tileZ = zs[j];
for (const tileRepresentation of representationsByZ[tileZ]) {
const tileCoord = tileRepresentation.tile.tileCoord;
const tileCoordKey = getTileCoordKey(tileCoord);
// do not render the tile mask if alpha < 1
if (tileCoordKey in alphaLookup) {
continue;
}
const tileExtent = tileGrid.getTileCoordExtent(tileCoord);
this.renderTileMask(
/** @type {TileRepresentation} */ (tileRepresentation),
tileZ,
tileExtent,
depthForZ(tileZ)
);
}
}
}

this.beforeTilesRender(frameState, blend);

for (let j = 0, jj = zs.length; j < jj; ++j) {
const tileZ = zs[j];
for (const tileRepresentation of representationsByZ[tileZ]) {
Expand Down Expand Up @@ -739,7 +782,7 @@ class WebGLBaseTileLayerRenderer extends WebGLLayerRenderer {
if (tileRepresentationCache.containsKey(cacheKey)) {
const tileRepresentation = tileRepresentationCache.get(cacheKey);
if (
tileRepresentation.loaded &&
tileRepresentation.ready &&
!lookupHasTile(tileRepresentationLookup, tileRepresentation.tile)
) {
addTileRepresentationToLookup(
Expand Down