maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
110 lines (93 loc) • 5.08 kB
text/typescript
import {Texture} from '../texture.ts';
import {DepthMode} from '../depth_mode.ts';
import {CullFaceMode} from '../cull_face_mode.ts';
import {
colorReliefUniformValues
} from '../program/color_relief_program.ts';
import type {FrameRenderContext} from '../../render/frame_render_context.ts';
import type {ColorMode} from '../color_mode.ts';
import type {StencilMode} from '../stencil_mode.ts';
import type {Painter} from '../../render/painter.ts';
import type {TileManager} from '../../tile/tile_manager.ts';
import type {ColorReliefStyleLayer} from '../../style/style_layer/color_relief_style_layer.ts';
import type {OverscaledTileID} from '../../tile/tile_id.ts';
export function drawColorRelief(painter: Painter, tileManager: TileManager, layer: ColorReliefStyleLayer, tileIDs: OverscaledTileID[], frameRenderContext: FrameRenderContext): void {
if (frameRenderContext.currentPass !== 'translucent') return;
if (!tileIDs.length) return;
const {useSubdivision} = frameRenderContext.data;
const depthMode = frameRenderContext.getDepthModeForSublayer(0, DepthMode.ReadOnly);
const colorMode = frameRenderContext.colorModeForRenderPass();
// Globe (or any projection with subdivision) needs two-pass rendering to avoid artifacts when rendering texture tiles.
// See comments in draw_raster.ts for more details.
if (useSubdivision) {
// Two-pass rendering
const [stencilBorderless, stencilBorders, coords] = frameRenderContext.stencilConfigForOverlapTwoPass(tileIDs);
renderColorRelief(painter, tileManager, layer, coords, stencilBorderless, depthMode, colorMode, false, frameRenderContext); // draw without borders
renderColorRelief(painter, tileManager, layer, coords, stencilBorders, depthMode, colorMode, true, frameRenderContext); // draw with borders
} else {
// Simple rendering
const [stencil, coords] = frameRenderContext.getStencilConfigForOverlapAndUpdateStencilID(tileIDs);
renderColorRelief(painter, tileManager, layer, coords, stencil, depthMode, colorMode, false, frameRenderContext);
}
}
let textureMaxSize = 0;
/**
* Draws the color-relief tiles of one pass.
*
* A loaded raster-DEM tile can carry no DEM at all (e.g. an empty 204 response);
* such tiles are skipped before the first-tile setup reads from them.
*/
function renderColorRelief(
painter: Painter,
tileManager: TileManager,
layer: ColorReliefStyleLayer,
coords: OverscaledTileID[],
stencilModes: {[_: number]: Readonly<StencilMode>},
depthMode: Readonly<DepthMode>,
colorMode: Readonly<ColorMode>,
useBorder: boolean,
frameRenderContext: FrameRenderContext
) {
const projection = painter.style.projection;
const context = painter.context;
const gl = context.gl;
const program = frameRenderContext.useProgram('colorRelief');
const align = !frameRenderContext.data.moving;
const textureFilter = layer.paint.get('resampling') === 'nearest' ? gl.NEAREST : gl.LINEAR;
let firstTile = true;
let colorRampSize = 0;
for (const coord of coords) {
const tile = tileManager.getTile(coord);
const dem = tile.dem;
if (!dem?.data) {
continue;
}
if(firstTile) {
// we should avoid calling gl.getParameter at runtime (GPU stall risk)
textureMaxSize ||= gl.getParameter(gl.MAX_TEXTURE_SIZE);
const maxLength = textureMaxSize;
const {elevationTexture, colorTexture} = layer.getColorRampTextures(context, maxLength, dem.getUnpackVector());
context.activeTexture.set(gl.TEXTURE1);
elevationTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
context.activeTexture.set(gl.TEXTURE4);
colorTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
firstTile = false;
colorRampSize = elevationTexture.size[0];
}
const textureStride = dem.stride;
context.activeTexture.set(gl.TEXTURE0);
if (!tile.demTexture || tile.needsColorReliefPrepare) {
context.pixelStoreUnpackPremultiplyAlpha.set(false);
tile.demTexture ||= painter.getTileTexture(textureStride) ??
new Texture(context, {width: textureStride, height: textureStride, data: null}, gl.RGBA);
tile.demTexture.update(dem.getPixels(), {premultiply: false});
tile.needsColorReliefPrepare = false;
}
tile.demTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true, 'raster');
const terrainData = frameRenderContext.getTerrainDataForTile(coord);
const projectionData = frameRenderContext.getProjectionDataForTile(coord, {aligned: align});
program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.backCCW,
colorReliefUniformValues(layer, tile.dem, colorRampSize), terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
}
}