maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
95 lines (80 loc) • 4.58 kB
text/typescript
import {DepthMode} from '../depth_mode.ts';
import {StencilMode} from '../stencil_mode.ts';
import {CullFaceMode} from '../cull_face_mode.ts';
import {layerOpacityUniformValues} from '../program/layer_opacity_program.ts';
import {Color} from '@maplibre/maplibre-gl-style-spec';
import type {Painter} from '../../render/painter.ts';
import type {LineStyleLayer} from '../../style/style_layer/line_style_layer.ts';
import type {FillStyleLayer} from '../../style/style_layer/fill_style_layer.ts';
import type {OverscaledTileID} from '../../tile/tile_id.ts';
export type PrepareDrawLayerOpacityResult = {
compositeTarget: WebGLFramebuffer;
compositeViewport: [number, number, number, number];
};
/**
* Partial line-layer-opacity
* render the whole layer to a scratch FBO, then composite with `layerOpacity`.
* Applies opacity uniformly to the layer instead of accumulating alpha across overlapping segments.
*/
export function prepareDrawLayerOpacity(painter: Painter, layer: LineStyleLayer | FillStyleLayer, coords: OverscaledTileID[], terrain: boolean): PrepareDrawLayerOpacityResult {
const context = painter.context;
const compositeTarget = context.bindFramebuffer.get();
const compositeViewport = context.viewport.get();
const [, , width, height] = compositeViewport;
bindLayerOpacity(painter, width, height);
context.viewport.set([0, 0, width, height]);
context.clear({color: Color.transparent, depth: 1, stencil: 0});
painter.currentStencilSource = undefined;
painter.renderTileClippingMasks(layer, coords, terrain);
return {
compositeTarget,
compositeViewport
};
}
function bindLayerOpacity(painter: Painter, width: number, height: number): void {
const gl = painter.context.gl;
if (!painter.layerOpacityFbo) {
const fbo = painter.context.createFramebuffer(width, height, true, true);
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
fbo.colorAttachment.set(texture);
fbo.depthAttachment.set(painter.context.createRenderbuffer(gl.DEPTH_STENCIL, width, height));
painter.layerOpacityFbo = fbo;
painter.context.bindFramebuffer.set(painter.layerOpacityFbo.framebuffer);
return;
}
if (painter.layerOpacityFbo.width === width && painter.layerOpacityFbo.height === height) {
painter.context.bindFramebuffer.set(painter.layerOpacityFbo.framebuffer);
return;
}
const fbo = painter.layerOpacityFbo;
gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
painter.context.bindRenderbuffer.set(fbo.depthAttachment.get());
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
painter.context.bindRenderbuffer.set(null);
fbo.width = width;
fbo.height = height;
painter.context.bindFramebuffer.set(fbo.framebuffer);
}
export function drawLayerOpacity(painter: Painter, opacity: number, prepareDrawLayerOpacityResult: PrepareDrawLayerOpacityResult, layer: LineStyleLayer | FillStyleLayer): void {
const context = painter.context;
const gl = context.gl;
context.bindFramebuffer.set(prepareDrawLayerOpacityResult.compositeTarget);
context.viewport.set(prepareDrawLayerOpacityResult.compositeViewport);
context.activeTexture.set(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, painter.layerOpacityFbo.colorAttachment.get());
painter.useProgram('layerOpacity').draw(context, gl.TRIANGLES,
DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled,
layerOpacityUniformValues(opacity, 0), null, null,
layer.id, painter.viewportBuffer, painter.quadTriangleIndexBuffer,
painter.viewportSegments, layer.paint, painter.transform.zoom);
// Clipping masks were drawn into the scratch FBO's stencil buffer, not the composite target's.
// Reset currentStencilSource so a later layer on the same source redraws its masks into the composite target instead of reusing stale ones.
painter.currentStencilSource = undefined;
}