diff --git a/CHANGELOG.md b/CHANGELOG.md index e545102be3..31f76e4873 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - ⚠️ Remove unminified prod build ([#4906](https://github.com/maplibre/maplibre-gl-js/pull/4906)) - Fix issue where raster tile source won't fetch updates following request error ([#4890](https://github.com/maplibre/maplibre-gl-js/pull/4890)) - Fix 3D models in custom layers not being properly occluded by the globe ([#4817](https://github.com/maplibre/maplibre-gl-js/issues/4817)) +- Fix issue where raster tiles were not rendered correctly when using globe and terrain ([#4912](https://github.com/maplibre/maplibre-gl-js/pull/4912)) - _...Add new stuff here..._ ## v5.0.0-pre.3 diff --git a/src/geo/projection/globe.ts b/src/geo/projection/globe.ts index 4f4e28bece..58f3fcd889 100644 --- a/src/geo/projection/globe.ts +++ b/src/geo/projection/globe.ts @@ -12,10 +12,6 @@ import {ProjectionErrorMeasurement} from './globe_projection_error_measurement'; import {createTileMeshWithBuffers, CreateTileMeshOptions} from '../../util/create_tile_mesh'; export const globeConstants = { - /** - * The size of border region for stencil masks, in internal tile coordinates. - * Used for globe rendering. - */ globeTransitionTimeSeconds: 0.5, maxGlobeZoom: 12.0, errorTransitionTimeSeconds: 0.5 diff --git a/src/geo/projection/globe_transform.test.ts b/src/geo/projection/globe_transform.test.ts index 151057ac8e..18738697a7 100644 --- a/src/geo/projection/globe_transform.test.ts +++ b/src/geo/projection/globe_transform.test.ts @@ -41,16 +41,26 @@ describe('GlobeTransform', () => { describe('getProjectionData', () => { const globeTransform = createGlobeTransform(globeProjectionMock); test('mercator tile extents are set', () => { - const projectionData = globeTransform.getProjectionData(new OverscaledTileID(1, 0, 1, 1, 0)); + const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0)}); expectToBeCloseToArray(projectionData.tileMercatorCoords, [0.5, 0, 0.5 / EXTENT, 0.5 / EXTENT]); }); + + test('Globe transition is not 0 when not ignoring the globe matrix', () => { + const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0)}); + expect(projectionData.projectionTransition).not.toBe(0); + }); + + test('Ignoring the globe matrix sets transition to 0', () => { + const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(1, 0, 1, 1, 0), ignoreGlobeMatrix: true}); + expect(projectionData.projectionTransition).toBe(0); + }); }); describe('clipping plane', () => { const globeTransform = createGlobeTransform(globeProjectionMock); describe('general plane properties', () => { - const projectionData = globeTransform.getProjectionData(new OverscaledTileID(0, 0, 0, 0, 0)); + const projectionData = globeTransform.getProjectionData({overscaledTileID: new OverscaledTileID(0, 0, 0, 0, 0)}); test('plane vector length', () => { const len = Math.sqrt( diff --git a/src/geo/projection/globe_transform.ts b/src/geo/projection/globe_transform.ts index c1c1b6e991..41ff63f429 100644 --- a/src/geo/projection/globe_transform.ts +++ b/src/geo/projection/globe_transform.ts @@ -16,7 +16,7 @@ import {PaddingOptions} from '../edge_insets'; import {tileCoordinatesToMercatorCoordinates} from './mercator_utils'; import {angularCoordinatesToSurfaceVector, getGlobeRadiusPixels, getZoomAdjustment, mercatorCoordinatesToAngularCoordinatesRadians, projectTileCoordinatesToSphere, sphereSurfacePointToCoordinates} from './globe_utils'; import {EXTENT} from '../../data/extent'; -import type {ProjectionData} from './projection_data'; +import type {ProjectionData, ProjectionDataParams} from './projection_data'; import {globeCoveringTiles} from './globe_covering_tiles'; import {Frustum} from '../../util/primitives'; @@ -448,8 +448,9 @@ export class GlobeTransform implements ITransform { return (this._lastUpdateTimeSeconds - this._lastGlobeChangeTimeSeconds) < globeConstants.globeTransitionTimeSeconds; } - getProjectionData(overscaledTileID: OverscaledTileID, aligned?: boolean, ignoreTerrainMatrix?: boolean): ProjectionData { - const data = this._mercatorTransform.getProjectionData(overscaledTileID, aligned, ignoreTerrainMatrix); + getProjectionData(params: ProjectionDataParams): ProjectionData { + const {overscaledTileID, aligned, ignoreTerrainMatrix, ignoreGlobeMatrix} = params; + const data = this._mercatorTransform.getProjectionData({overscaledTileID, aligned, ignoreTerrainMatrix}); // Set 'projectionMatrix' to actual globe transform if (this.isGlobeRendering) { @@ -457,7 +458,7 @@ export class GlobeTransform implements ITransform { } data.clippingPlane = this._cachedClippingPlane as [number, number, number, number]; - data.projectionTransition = this._globeness; + data.projectionTransition = ignoreGlobeMatrix ? 0 : this._globeness; return data; } @@ -1181,7 +1182,7 @@ export class GlobeTransform implements ITransform { } getProjectionDataForCustomLayer(): ProjectionData { - const projectionData = this.getProjectionData(new OverscaledTileID(0, 0, 0, 0, 0)); + const projectionData = this.getProjectionData({overscaledTileID: new OverscaledTileID(0, 0, 0, 0, 0)}); projectionData.tileMercatorCoords = [0, 0, 1, 1]; // Even though we requested projection data for the mercator base tile which covers the entire mercator range, diff --git a/src/geo/projection/mercator_transform.ts b/src/geo/projection/mercator_transform.ts index d75df8c0c5..b2dfd81a98 100644 --- a/src/geo/projection/mercator_transform.ts +++ b/src/geo/projection/mercator_transform.ts @@ -12,7 +12,7 @@ import {CoveringTilesOptions, CoveringZoomOptions, IReadonlyTransform, ITransfor import {PaddingOptions} from '../edge_insets'; import {mercatorCoordinateToLocation, getBasicProjectionData, getMercatorHorizon, locationToMercatorCoordinate, projectToWorldCoordinates, unprojectFromWorldCoordinates, calculateTileMatrix, maxMercatorHorizonAngle, cameraMercatorCoordinateFromCenterAndRotation} from './mercator_utils'; import {EXTENT} from '../../data/extent'; -import type {ProjectionData} from './projection_data'; +import type {ProjectionData, ProjectionDataParams} from './projection_data'; import {scaleZoom, TransformHelper, zoomScale} from '../transform_helper'; import {mercatorCoveringTiles} from './mercator_covering_tiles'; @@ -750,7 +750,8 @@ export class MercatorTransform implements ITransform { return false; } - getProjectionData(overscaledTileID: OverscaledTileID, aligned?: boolean, ignoreTerrainMatrix?: boolean): ProjectionData { + getProjectionData(params: ProjectionDataParams): ProjectionData { + const {overscaledTileID, aligned, ignoreTerrainMatrix} = params; const matrix = overscaledTileID ? this.calculatePosMatrix(overscaledTileID, aligned) : null; return getBasicProjectionData(overscaledTileID, matrix, ignoreTerrainMatrix); } @@ -826,7 +827,7 @@ export class MercatorTransform implements ITransform { getProjectionDataForCustomLayer(): ProjectionData { const tileID = new OverscaledTileID(0, 0, 0, 0, 0); - const projectionData = this.getProjectionData(tileID, false, true); + const projectionData = this.getProjectionData({overscaledTileID: tileID, ignoreTerrainMatrix: true}); const tileMatrix = calculateTileMatrix(tileID, this.worldSize); mat4.multiply(tileMatrix, this._viewProjMatrix, tileMatrix); diff --git a/src/geo/projection/projection_data.ts b/src/geo/projection/projection_data.ts index f2c41c9828..0f880c745c 100644 --- a/src/geo/projection/projection_data.ts +++ b/src/geo/projection/projection_data.ts @@ -1,4 +1,5 @@ import type {mat4} from 'gl-matrix'; +import type {OverscaledTileID} from '../../source/tile_id'; /** * This type contains all data necessary to project a tile to screen in MapLibre's shader system. @@ -44,3 +45,22 @@ export type ProjectionData = { */ fallbackMatrix: mat4; } + +export type ProjectionDataParams = { + /** + * The ID of the current tile + */ + overscaledTileID: OverscaledTileID | null; + /** + * Set to true if a pixel-aligned matrix should be used, if possible (mostly used for raster tiles under mercator projection) + */ + aligned?: boolean; + /** + * Set to true if the terrain matrix should be ignored + */ + ignoreTerrainMatrix?: boolean; + /** + * Set to true if the globe matrix should be ignored (i.e. when rendering to texture for terrain) + */ + ignoreGlobeMatrix?: boolean; +} diff --git a/src/geo/transform_interface.ts b/src/geo/transform_interface.ts index 5fdf3cdcfb..e316bcd0fa 100644 --- a/src/geo/transform_interface.ts +++ b/src/geo/transform_interface.ts @@ -8,7 +8,7 @@ import type {PaddingOptions} from './edge_insets'; import {Terrain} from '../render/terrain'; import {PointProjection} from '../symbol/projection'; import {MapProjectionEvent} from '../ui/events'; -import type {ProjectionData} from './projection/projection_data'; +import type {ProjectionData, ProjectionDataParams} from './projection/projection_data'; export type CoveringZoomOptions = { /** @@ -437,10 +437,9 @@ export interface IReadonlyTransform extends ITransformGetters { /** * @internal * Generates a `ProjectionData` instance to be used while rendering the supplied tile. - * @param overscaledTileID - The ID of the current tile. - * @param aligned - Set to true if a pixel-aligned matrix should be used, if possible (mostly used for raster tiles under mercator projection). + * @param params - Parameters for the projection data generation. */ - getProjectionData(overscaledTileID: OverscaledTileID, aligned?: boolean, ignoreTerrainMatrix?: boolean): ProjectionData; + getProjectionData(params: ProjectionDataParams): ProjectionData; /** * @internal diff --git a/src/render/draw_background.ts b/src/render/draw_background.ts index 1db26599eb..bd09a2ce78 100644 --- a/src/render/draw_background.ts +++ b/src/render/draw_background.ts @@ -42,7 +42,7 @@ export function drawBackground(painter: Painter, sourceCache: SourceCache, layer const crossfade = layer.getCrossfadeParameters(); for (const tileID of tileIDs) { - const projectionData = transform.getProjectionData(tileID); + const projectionData = transform.getProjectionData({overscaledTileID: tileID}); const uniformValues = image ? backgroundPatternUniformValues(opacity, painter, image, {tileID, tileSize}, crossfade) : diff --git a/src/render/draw_circle.ts b/src/render/draw_circle.ts index 7960ff99f7..63fe26c6e7 100644 --- a/src/render/draw_circle.ts +++ b/src/render/draw_circle.ts @@ -80,7 +80,7 @@ export function drawCircles(painter: Painter, sourceCache: SourceCache, layer: C const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord); const uniformValues = circleUniformValues(painter, tile, layer, translateForUniforms, radiusCorrectionFactor); - const projectionData = transform.getProjectionData(coord); + const projectionData = transform.getProjectionData({overscaledTileID: coord}); const state: TileRenderState = { programConfiguration, diff --git a/src/render/draw_collision_debug.ts b/src/render/draw_collision_debug.ts index 99f362792b..9da839118c 100644 --- a/src/render/draw_collision_debug.ts +++ b/src/render/draw_collision_debug.ts @@ -62,7 +62,7 @@ export function drawCollisionDebug(painter: Painter, sourceCache: SourceCache, l CullFaceMode.disabled, collisionUniformValues(painter.transform), painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord), - transform.getProjectionData(coord), + transform.getProjectionData({overscaledTileID: coord}), layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer, buffers.segments, null, painter.transform.zoom, null, null, buffers.collisionVertexBuffer); diff --git a/src/render/draw_debug.ts b/src/render/draw_debug.ts index 80551e241d..8974d8f9e9 100644 --- a/src/render/draw_debug.ts +++ b/src/render/draw_debug.ts @@ -91,7 +91,7 @@ function drawDebugTile(painter: Painter, sourceCache: SourceCache, coord: Oversc const tileLabel = `${tileIdText} ${tileSizeKb}kB`; drawTextToOverlay(painter, tileLabel); - const projectionData = painter.transform.getProjectionData(coord); + const projectionData = painter.transform.getProjectionData({overscaledTileID: coord}); program.draw(context, gl.TRIANGLES, depthMode, stencilMode, ColorMode.alphaBlended, CullFaceMode.disabled, debugUniformValues(Color.transparent, scaleRatio), null, projectionData, id, diff --git a/src/render/draw_fill.ts b/src/render/draw_fill.ts index 0b23b5b572..fb2ebba7ce 100644 --- a/src/render/draw_fill.ts +++ b/src/render/draw_fill.ts @@ -107,7 +107,7 @@ function drawFillTiles( updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer); - const projectionData = transform.getProjectionData(coord); + const projectionData = transform.getProjectionData({overscaledTileID: coord}); const translateForUniforms = translatePosition(transform, tile, propertyFillTranslate, propertyFillTranslateAnchor); diff --git a/src/render/draw_fill_extrusion.ts b/src/render/draw_fill_extrusion.ts index 02c2d8f457..021778b29b 100644 --- a/src/render/draw_fill_extrusion.ts +++ b/src/render/draw_fill_extrusion.ts @@ -79,7 +79,7 @@ function drawExtrusionTiles( programConfiguration.updatePaintBuffers(crossfade); } - const projectionData = transform.getProjectionData(coord); + const projectionData = transform.getProjectionData({overscaledTileID: coord}); updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer); const translate = translatePosition( diff --git a/src/render/draw_heatmap.ts b/src/render/draw_heatmap.ts index c28b103a90..4bea505487 100644 --- a/src/render/draw_heatmap.ts +++ b/src/render/draw_heatmap.ts @@ -79,7 +79,7 @@ function prepareHeatmapFlat(painter: Painter, sourceCache: SourceCache, layer: H const programConfiguration = bucket.programConfigurations.get(layer.id); const program = painter.useProgram('heatmap', programConfiguration); - const projectionData = transform.getProjectionData(coord); + const projectionData = transform.getProjectionData({overscaledTileID: coord}); const radiusCorrectionFactor = transform.getCircleRadiusCorrection(); @@ -145,7 +145,7 @@ function prepareHeatmapTerrain(painter: Painter, tile: Tile, layer: HeatmapStyle const programConfiguration = bucket.programConfigurations.get(layer.id); const program = painter.useProgram('heatmap', programConfiguration, true); - const projectionData = painter.transform.getProjectionData(tile.tileID); + const projectionData = painter.transform.getProjectionData({overscaledTileID: tile.tileID}); const terrainData = painter.style.map.terrain.getTerrainData(coord); program.draw(context, gl.TRIANGLES, DepthMode.disabled, stencilMode, colorMode, CullFaceMode.disabled, @@ -177,7 +177,7 @@ function renderHeatmapTerrain(painter: Painter, layer: HeatmapStyleLayer, coord: context.activeTexture.set(gl.TEXTURE1); colorRampTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); - const projectionData = transform.getProjectionData(coord, false, true); + const projectionData = transform.getProjectionData({overscaledTileID: coord, ignoreTerrainMatrix: true}); painter.useProgram('heatmapTexture').draw(context, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, diff --git a/src/render/draw_hillshade.ts b/src/render/draw_hillshade.ts index 6469ea153c..b6f1d8e5e1 100644 --- a/src/render/draw_hillshade.ts +++ b/src/render/draw_hillshade.ts @@ -73,7 +73,7 @@ function renderHillshade( context.activeTexture.set(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get()); - const projectionData = transform.getProjectionData(coord, align); + const projectionData = transform.getProjectionData({overscaledTileID: coord, aligned: align}); program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.backCCW, hillshadeUniformValues(painter, tile, layer), terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments); diff --git a/src/render/draw_line.ts b/src/render/draw_line.ts index 0a8b0b1d9d..38c4cbf5a8 100644 --- a/src/render/draw_line.ts +++ b/src/render/draw_line.ts @@ -67,7 +67,7 @@ export function drawLine(painter: Painter, sourceCache: SourceCache, layer: Line if (posTo && posFrom) programConfiguration.setConstantPatternPositions(posTo, posFrom); } - const projectionData = transform.getProjectionData(coord); + const projectionData = transform.getProjectionData({overscaledTileID: coord}); const pixelRatio = transform.getPixelScale(); const uniformValues = image ? linePatternUniformValues(painter, tile, layer, pixelRatio, crossfade) : diff --git a/src/render/draw_raster.ts b/src/render/draw_raster.ts index ccff8b98ff..2abb08eddb 100644 --- a/src/render/draw_raster.ts +++ b/src/render/draw_raster.ts @@ -21,7 +21,7 @@ const cornerCoords = [ new Point(0, EXTENT), ]; -export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array) { +export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array, isRenderingToTexture: boolean = false) { if (painter.renderPass !== 'translucent') return; if (layer.paint.get('raster-opacity') === 0) return; if (!tileIDs.length) return; @@ -43,16 +43,16 @@ export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: Ra // Stencil mask and two-pass is not used for ImageSource sources regardless of projection. if (source instanceof ImageSource) { // Image source - no stencil is used - drawTiles(painter, sourceCache, layer, tileIDs, null, false, false, source.tileCoords, source.flippedWindingOrder); + drawTiles(painter, sourceCache, layer, tileIDs, null, false, false, source.tileCoords, source.flippedWindingOrder, isRenderingToTexture); } else if (useSubdivision) { // Two-pass rendering const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs); - drawTiles(painter, sourceCache, layer, coords, stencilBorderless, false, true, cornerCoords); // draw without borders - drawTiles(painter, sourceCache, layer, coords, stencilBorders, true, true, cornerCoords); // draw with borders + drawTiles(painter, sourceCache, layer, coords, stencilBorderless, false, true, cornerCoords, false, isRenderingToTexture); // draw without borders + drawTiles(painter, sourceCache, layer, coords, stencilBorders, true, true, cornerCoords, false, isRenderingToTexture); // draw with borders } else { // Simple rendering const [stencil, coords] = painter.stencilConfigForOverlap(tileIDs); - drawTiles(painter, sourceCache, layer, coords, stencil, false, true, cornerCoords); + drawTiles(painter, sourceCache, layer, coords, stencil, false, true, cornerCoords, false, isRenderingToTexture); } } @@ -65,7 +65,8 @@ function drawTiles( useBorder: boolean, allowPoles: boolean, corners: Array, - flipCullfaceMode: boolean = false) { + flipCullfaceMode: boolean = false, + isRenderingToTexture: boolean = false) { const minTileZ = coords[coords.length - 1].overscaledZ; const context = painter.context; @@ -120,7 +121,7 @@ function drawTiles( } const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord); - const projectionData = transform.getProjectionData(coord, align); + const projectionData = transform.getProjectionData({overscaledTileID: coord, aligned: align, ignoreGlobeMatrix: isRenderingToTexture}); const uniformValues = rasterUniformValues(parentTL || [0, 0], parentScaleBy || 1, fade, layer, corners); const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, allowPoles, 'raster'); diff --git a/src/render/draw_sky.ts b/src/render/draw_sky.ts index 27b4d0c357..83b28b5b5f 100644 --- a/src/render/draw_sky.ts +++ b/src/render/draw_sky.ts @@ -85,7 +85,7 @@ export function drawAtmosphere(painter: Painter, sky: Sky, light: Light) { const sunPos = getSunPos(light, painter.transform); - const projectionData = transform.getProjectionData(null); + const projectionData = transform.getProjectionData({overscaledTileID: null}); const atmosphereBlend = sky.properties.get('atmosphere-blend') * projectionData.projectionTransition; if (atmosphereBlend === 0) { diff --git a/src/render/draw_symbol.ts b/src/render/draw_symbol.ts index 550ec1b77f..bef88de127 100644 --- a/src/render/draw_symbol.ts +++ b/src/render/draw_symbol.ts @@ -379,7 +379,7 @@ function drawLayerSymbols( const glCoordMatrixForShader = getGlCoordMatrix(pitchWithMap, rotateWithMap, painter.transform, s); const translation = translatePosition(transform, tile, translate, translateAnchor); - const projectionData = transform.getProjectionData(coord); + const projectionData = transform.getProjectionData({overscaledTileID: coord}); const hasVariableAnchors = hasVariablePlacement && bucket.hasTextData(); const updateTextFitIcon = layer.layout.get('icon-text-fit') !== 'none' && diff --git a/src/render/draw_terrain.ts b/src/render/draw_terrain.ts index ce5bcd5e02..68c24b9d1f 100644 --- a/src/render/draw_terrain.ts +++ b/src/render/draw_terrain.ts @@ -27,7 +27,7 @@ function drawDepth(painter: Painter, terrain: Terrain) { context.clear({color: Color.transparent, depth: 1}); for (const tile of tiles) { const terrainData = terrain.getTerrainData(tile.tileID); - const projectionData = tr.getProjectionData(tile.tileID, false, true); + const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, ignoreTerrainMatrix: true}); const uniformValues = terrainDepthUniformValues(terrain.getMeshFrameDelta(tr.zoom)); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments); } @@ -61,7 +61,7 @@ function drawCoords(painter: Painter, terrain: Terrain) { context.activeTexture.set(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, coords.texture); const uniformValues = terrainCoordsUniformValues(255 - terrain.coordsIndex.length, terrain.getMeshFrameDelta(tr.zoom)); - const projectionData = tr.getProjectionData(tile.tileID, false, true); + const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, ignoreTerrainMatrix: true}); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments); terrain.coordsIndex.push(tile.tileID.key); } @@ -89,7 +89,7 @@ function drawTerrain(painter: Painter, terrain: Terrain, tiles: Array) { const eleDelta = terrain.getMeshFrameDelta(tr.zoom); const fogMatrix = tr.calculateFogMatrix(tile.tileID.toUnwrapped()); const uniformValues = terrainUniformValues(eleDelta, fogMatrix, painter.style.sky, tr.pitch); - const projectionData = tr.getProjectionData(tile.tileID, false, true); + const projectionData = tr.getProjectionData({overscaledTileID: tile.tileID, ignoreTerrainMatrix: true}); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments); } } diff --git a/src/render/painter.ts b/src/render/painter.ts index 741fb535f1..7d4b102750 100644 --- a/src/render/painter.ts +++ b/src/render/painter.ts @@ -297,7 +297,7 @@ export class Painter { const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, useBorders, true, 'stencil'); - const projectionData = transform.getProjectionData(tileID); + const projectionData = transform.getProjectionData({overscaledTileID: tileID}); program.draw(context, gl.TRIANGLES, DepthMode.disabled, // Tests will always pass, and ref value will be written to stencil buffer. @@ -327,7 +327,7 @@ export class Painter { const terrainData = this.style.map.terrain && this.style.map.terrain.getTerrainData(tileID); const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, true, true, 'raster'); - const projectionData = transform.getProjectionData(tileID); + const projectionData = transform.getProjectionData({overscaledTileID: tileID}); program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, ColorMode.disabled, CullFaceMode.backCCW, null, @@ -632,7 +632,7 @@ export class Painter { drawCoords(this, this.style.map.terrain); } - renderLayer(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array) { + renderLayer(painter: Painter, sourceCache: SourceCache, layer: StyleLayer, coords: Array, isRenderingToTexture: boolean = false) { if (layer.isHidden(this.transform.zoom)) return; if (layer.type !== 'background' && layer.type !== 'custom' && !(coords || []).length) return; this.id = layer.id; @@ -660,7 +660,7 @@ export class Painter { drawHillshade(painter, sourceCache, layer as any, coords); break; case 'raster': - drawRaster(painter, sourceCache, layer as any, coords); + drawRaster(painter, sourceCache, layer as any, coords, isRenderingToTexture); break; case 'background': drawBackground(painter, sourceCache, layer as any, coords); diff --git a/src/render/render_to_texture.test.ts b/src/render/render_to_texture.test.ts index e8e00421f4..0f807ce680 100644 --- a/src/render/render_to_texture.test.ts +++ b/src/render/render_to_texture.test.ts @@ -108,7 +108,7 @@ describe('render to texture', () => { test('check state', () => { expect(rtt._renderableTiles.map(t => t.tileID.key)).toStrictEqual(['923']); - expect(rtt._coordsDescendingInv).toEqual({ + expect(rtt._coordsAscending).toEqual({ 'maine': { '923': [ { @@ -126,7 +126,7 @@ describe('render to texture', () => { ] } }); - expect(rtt._coordsDescendingInvStr).toStrictEqual({maine: {'923': '923'}}); + expect(rtt._coordsAscendingStr).toStrictEqual({maine: {'923': '923'}}); }); test('should render text after a line by not adding the text to the stack', () => { diff --git a/src/render/render_to_texture.ts b/src/render/render_to_texture.ts index 16ec56d04b..8c4fd96b30 100644 --- a/src/render/render_to_texture.ts +++ b/src/render/render_to_texture.ts @@ -29,15 +29,15 @@ export class RenderToTexture { terrain: Terrain; pool: RenderPool; /** - * coordsDescendingInv contains a list of all tiles which should be rendered for one render-to-texture tile + * coordsAscending contains a list of all tiles which should be rendered for one render-to-texture tile * e.g. render 4 raster-tiles with size 256px to the 512px render-to-texture tile */ - _coordsDescendingInv: {[_: string]: {[_:string]: Array}}; + _coordsAscending: {[_: string]: {[_:string]: Array}}; /** * create a string representation of all to tiles rendered to render-to-texture tiles * this string representation is used to check if tile should be re-rendered. */ - _coordsDescendingInvStr: {[_: string]: {[_:string]: string}}; + _coordsAscendingStr: {[_: string]: {[_:string]: string}}; /** * store for render-stacks * a render stack is a set of layers which should be rendered into one texture @@ -83,36 +83,36 @@ export class RenderToTexture { this._renderableTiles = this.terrain.sourceCache.getRenderableTiles(); this._renderableLayerIds = style._order.filter(id => !style._layers[id].isHidden(zoom)); - this._coordsDescendingInv = {}; + this._coordsAscending = {}; for (const id in style.sourceCaches) { - this._coordsDescendingInv[id] = {}; + this._coordsAscending[id] = {}; const tileIDs = style.sourceCaches[id].getVisibleCoordinates(); for (const tileID of tileIDs) { const keys = this.terrain.sourceCache.getTerrainCoords(tileID); for (const key in keys) { - if (!this._coordsDescendingInv[id][key]) this._coordsDescendingInv[id][key] = []; - this._coordsDescendingInv[id][key].push(keys[key]); + if (!this._coordsAscending[id][key]) this._coordsAscending[id][key] = []; + this._coordsAscending[id][key].push(keys[key]); } } } - this._coordsDescendingInvStr = {}; + this._coordsAscendingStr = {}; for (const id of style._order) { const layer = style._layers[id], source = layer.source; if (LAYERS[layer.type]) { - if (!this._coordsDescendingInvStr[source]) { - this._coordsDescendingInvStr[source] = {}; - for (const key in this._coordsDescendingInv[source]) - this._coordsDescendingInvStr[source][key] = this._coordsDescendingInv[source][key].map(c => c.key).sort().join(); + if (!this._coordsAscendingStr[source]) { + this._coordsAscendingStr[source] = {}; + for (const key in this._coordsAscending[source]) + this._coordsAscendingStr[source][key] = this._coordsAscending[source][key].map(c => c.key).sort().join(); } } } // check tiles to render for (const tile of this._renderableTiles) { - for (const source in this._coordsDescendingInvStr) { + for (const source in this._coordsAscendingStr) { // rerender if there are more coords to render than in the last rendering - const coords = this._coordsDescendingInvStr[source][tile.tileID.key]; + const coords = this._coordsAscendingStr[source][tile.tileID.key]; if (coords && coords !== tile.rttCoords[source]) tile.rtt = []; } } @@ -177,11 +177,11 @@ export class RenderToTexture { painter.currentStencilSource = undefined; for (let l = 0; l < layers.length; l++) { const layer = painter.style._layers[layers[l]]; - const coords = layer.source ? this._coordsDescendingInv[layer.source][tile.tileID.key] : [tile.tileID]; + const coords = layer.source ? this._coordsAscending[layer.source][tile.tileID.key] : [tile.tileID]; painter.context.viewport.set([0, 0, obj.fbo.width, obj.fbo.height]); painter._renderTileClippingMasks(layer, coords, true); - painter.renderLayer(painter, painter.style.sourceCaches[layer.source], layer, coords); - if (layer.source) tile.rttCoords[layer.source] = this._coordsDescendingInvStr[layer.source][tile.tileID.key]; + painter.renderLayer(painter, painter.style.sourceCaches[layer.source], layer, coords, true); + if (layer.source) tile.rttCoords[layer.source] = this._coordsAscendingStr[layer.source][tile.tileID.key]; } } drawTerrain(this.painter, this.terrain, this._rttTiles); diff --git a/src/style/style_layer/custom_style_layer.ts b/src/style/style_layer/custom_style_layer.ts index a1823e15d2..95c41410c8 100644 --- a/src/style/style_layer/custom_style_layer.ts +++ b/src/style/style_layer/custom_style_layer.ts @@ -86,7 +86,7 @@ export type CustomRenderMethodInput = { * For more details of this object's internals, see its doc comments in `src/geo/projection/projection_data.ts`. * * These uniforms are set so that `projectTile` in shader accepts a vec2 in range 0..1 in web mercator coordinates. - * Use `map.transform.getProjectionData(tileID)` to get uniforms for a given tile and pass vec2 in tile-local range 0..EXTENT instead. + * Use `map.transform.getProjectionData({overscaledTileID: tileID})` to get uniforms for a given tile and pass vec2 in tile-local range 0..EXTENT instead. * * For projection 3D features, use `projectTileFor3D` in the shader. * diff --git a/src/util/create_tile_mesh.ts b/src/util/create_tile_mesh.ts index 98b94b6351..94a18d16f0 100644 --- a/src/util/create_tile_mesh.ts +++ b/src/util/create_tile_mesh.ts @@ -6,6 +6,10 @@ import {NORTH_POLE_Y, SOUTH_POLE_Y} from '../render/subdivision'; import {EXTENT} from '../data/extent'; import posAttributes from '../data/pos_attributes'; +/** + * The size of border region for stencil masks, in internal tile coordinates. + * Used for globe rendering. + */ const EXTENT_STENCIL_BORDER = EXTENT / 128; /** diff --git a/test/examples/globe-custom-tiles.html b/test/examples/globe-custom-tiles.html index d87f8bdfd2..dadde378b9 100644 --- a/test/examples/globe-custom-tiles.html +++ b/test/examples/globe-custom-tiles.html @@ -262,7 +262,7 @@ } }; - const projectionData = map.transform.getProjectionData(tileID); + const projectionData = map.transform.getProjectionData({overscaledTileId: tileID}); gl.uniform4f( locations['u_projection_clipping_plane'], diff --git a/test/integration/render/tests/projection/globe/terrain/expected-ubuntu.png b/test/integration/render/tests/projection/globe/terrain/expected-ubuntu.png new file mode 100644 index 0000000000..d09dd71614 Binary files /dev/null and b/test/integration/render/tests/projection/globe/terrain/expected-ubuntu.png differ diff --git a/test/integration/render/tests/projection/globe/terrain/expected-windows.png b/test/integration/render/tests/projection/globe/terrain/expected-windows.png new file mode 100644 index 0000000000..b55c3f215a Binary files /dev/null and b/test/integration/render/tests/projection/globe/terrain/expected-windows.png differ diff --git a/test/integration/render/tests/projection/globe/terrain/expected.png b/test/integration/render/tests/projection/globe/terrain/expected.png new file mode 100644 index 0000000000..e81af98352 Binary files /dev/null and b/test/integration/render/tests/projection/globe/terrain/expected.png differ diff --git a/test/integration/render/tests/projection/globe/terrain/style.json b/test/integration/render/tests/projection/globe/terrain/style.json new file mode 100644 index 0000000000..f77251e933 --- /dev/null +++ b/test/integration/render/tests/projection/globe/terrain/style.json @@ -0,0 +1,53 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256, + "width": 256 + } + }, + "center": [ + 13.418056, + 52.499167 + ], + "zoom": 0, + "pitch": 0, + "sources": { + "terrain": { + "type": "raster-dem", + "tiles": ["local://tiles/{z}-{x}-{y}.terrain.png"], + "maxzoom": 15, + "tileSize": 256 + }, + "satellite": { + "type": "raster", + "tiles": ["local://tiles/{z}-{x}-{y}.satellite.png"], + "maxzoom": 17, + "tileSize": 256 + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "raster", + "type": "raster", + "source": "satellite", + "paint": { + "raster-opacity": 1.0 + } + } + ], + "terrain": { + "source": "terrain", + "exaggeration": 2 + }, + "projection": { + "type": "globe" + } +}