UNPKG

maplibre-gl

Version:

BSD licensed community fork of mapbox-gl, a WebGL interactive maps library

110 lines (93 loc) • 5.08 kB
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); } }