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

Improve terrain rendering when using globe projection #4825

Merged
merged 9 commits into from
Oct 29, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions src/geo/projection/globe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions src/geo/projection/globe_transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
11 changes: 6 additions & 5 deletions src/geo/projection/globe_transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -448,16 +448,17 @@ 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) {
data.mainMatrix = this._globeViewProjMatrix;
}

data.clippingPlane = this._cachedClippingPlane as [number, number, number, number];
data.projectionTransition = this._globeness;
data.projectionTransition = ignoreGlobeMatrix ? 0 : this._globeness;

return data;
}
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 4 additions & 3 deletions src/geo/projection/mercator_transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions src/geo/projection/projection_data.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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;
}
7 changes: 3 additions & 4 deletions src/geo/transform_interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
/**
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) :
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_circle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_collision_debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_fill_extrusion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
6 changes: 3 additions & 3 deletions src/render/draw_heatmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_hillshade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) :
Expand Down
15 changes: 8 additions & 7 deletions src/render/draw_raster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const cornerCoords = [
new Point(0, EXTENT),
];

export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array<OverscaledTileID>) {
export function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array<OverscaledTileID>, isRenderingToTexture: boolean = false) {
if (painter.renderPass !== 'translucent') return;
if (layer.paint.get('raster-opacity') === 0) return;
if (!tileIDs.length) return;
Expand All @@ -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);
}
}

Expand All @@ -65,7 +65,8 @@ function drawTiles(
useBorder: boolean,
allowPoles: boolean,
corners: Array<Point>,
flipCullfaceMode: boolean = false) {
flipCullfaceMode: boolean = false,
isRenderingToTexture: boolean = false) {
const minTileZ = coords[coords.length - 1].overscaledZ;

const context = painter.context;
Expand Down Expand Up @@ -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');
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_sky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' &&
Expand Down
Loading