Skip to content

Commit

Permalink
fill: opacity
Browse files Browse the repository at this point in the history
  • Loading branch information
LEON-MS93 committed Apr 18, 2022
1 parent fdbd58e commit f20692a
Show file tree
Hide file tree
Showing 17 changed files with 506 additions and 30 deletions.
93 changes: 93 additions & 0 deletions debug/polygon-opacity-per-layer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add a GeoJSON polygon</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='../dist/maplibre-gl-dev.js'></script>
<link href='../dist/maplibre-gl.css' rel='stylesheet' />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
const delta = -7;
var map = new maplibregl.Map({
container: 'map',
// style: {version: 8, layers: [], sources: {}},
style: 'https://demotiles.maplibre.org/style.json',
center: [-73 + delta, 34],
zoom: 7
});

map.on('load', function () {
map.addSource('shape1',
{
'type': 'geojson',
'data': {
'features': [
{
'type': 'Feature',
'properties': {
'color': 'red',
'height': 0,
'base_height': 0
},
'geometry': {
'type': 'MultiPolygon',
'coordinates': [
[// first polygon
[// first ring
[-73 + delta, 35], [-69 + delta, 35], [-69 + delta, 34], [-73 + delta, 34], [-73. + delta, 35]
]
]
]
}
},
{
'type': 'Feature',
'properties': {
'color': 'rgba(0,255,0,255)',
'height': 0,
'base_height': 0
},
'geometry': {
'type': 'MultiPolygon',
'coordinates': [
[// first polygon
[// first ring
[-74 + delta, 35], [-70 + delta, 35], [-70 + delta, 34], [-74 + delta, 34], [-74 + delta, 35]
]
]
]
}
}
],
'type': 'FeatureCollection'
}
}
);

const paint2d = {
// Get the fill-extrusion-color from the source 'color' property.
// 'fill-color': 'red',
'fill-color': ['get', 'color'],
// Make extrusions slightly opaque for see through indoor walls.
'fill-opacity': 0.5,
'fill-per-layer-opacity': true,
'fill-outline-color': 'rgba(255,0,0,1)'
};
map.addLayer({
'id': 'shape1',
'type': 'fill',
'source': 'shape1',
'paint': paint2d
});
});
</script>

</body>
</html>
128 changes: 99 additions & 29 deletions src/render/draw_fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import {
fillUniformValues,
fillPatternUniformValues,
fillOutlineUniformValues,
fillOutlinePatternUniformValues
fillOutlinePatternUniformValues,
fillfboUniformValues
} from './program/fill_program';

import type Painter from './painter';
import type SourceCache from '../source/source_cache';
import type FillStyleLayer from '../style/style_layer/fill_style_layer';
import type FillBucket from '../data/bucket/fill_bucket';
import type {OverscaledTileID} from '../source/tile_id';
import ColorMode from '../gl/color_mode';
import StencilMode from '../gl/stencil_mode';

export default drawFill;

Expand All @@ -24,39 +27,70 @@ function drawFill(painter: Painter, sourceCache: SourceCache, layer: FillStyleLa
return;
}

const colorMode = painter.colorModeForRenderPass();
let colorMode = painter.colorModeForRenderPass();

const pattern = layer.paint.get('fill-pattern');
const pass = painter.opaquePassEnabledForLayer() &&
(!pattern.constantOr(1 as any) &&
color.constantOr(Color.transparent).a === 1 &&
opacity.constantOr(0) === 1) ? 'opaque' : 'translucent';

// Draw fill
if (painter.renderPass === pass) {
const depthMode = painter.depthModeForSublayer(
1, painter.renderPass === 'opaque' ? DepthMode.ReadWrite : DepthMode.ReadOnly);
drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, false);
}
(!pattern.constantOr(1 as any) &&
color.constantOr(Color.transparent).a === 1 &&
opacity.constantOr(0) === 1) ? 'opaque' : 'translucent';

const perLayerOpacity = layer.paint.get('fill-per-layer-opacity') && pass === 'translucent';

if (perLayerOpacity) {
const gl = painter.context.gl;
if (painter.renderPass === 'offscreen') {
// prepare fbo
const context = painter.context;
// Turn on additive blending for kernels, which is a key aspect of kernel density estimation formula
colorMode = new ColorMode([gl.ONE, gl.ZERO], Color.transparent, [true, true, true, true]);
bindFramebuffer(context, painter, layer);
context.clear({color: Color.transparent});

const depthMode = DepthMode.disabled;
drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, false, true);
} else if (painter.renderPass === 'translucent') {
const context = painter.context;
context.setColorMode(painter.colorModeForRenderPass());

const fbo = layer.fillFbo;
if (!fbo) return;
context.activeTexture.set(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
const uniformValues = fillfboUniformValues(painter, 0, opacity.constantOr(1));
painter.useProgram('fillfbo').draw(context, gl.TRIANGLES,
DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled,
uniformValues,
layer.id, painter.viewportBuffer, painter.quadTriangleIndexBuffer,
painter.viewportSegments, layer.paint, painter.transform.zoom);
}
} else {
// Draw fill
if (painter.renderPass === pass) {
const depthMode = painter.depthModeForSublayer(
1, painter.renderPass === 'opaque' ? DepthMode.ReadWrite : DepthMode.ReadOnly);
drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, false, false);
}
// Draw stroke
if (painter.renderPass === 'translucent' && layer.paint.get('fill-antialias')) {

// If we defined a different color for the fill outline, we are
// going to ignore the bits in 0x07 and just care about the global
// clipping mask.
// Otherwise, we only want to drawFill the antialiased parts that are
// *outside* the current shape. This is important in case the fill
// or stroke color is translucent. If we wouldn't clip to outside
// the current shape, some pixels from the outline stroke overlapped
// the (non-antialiased) fill.
const depthMode = painter.depthModeForSublayer(
layer.getPaintProperty('fill-outline-color') ? 2 : 0, DepthMode.ReadOnly);
drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, true, false);
}

// Draw stroke
if (painter.renderPass === 'translucent' && layer.paint.get('fill-antialias')) {

// If we defined a different color for the fill outline, we are
// going to ignore the bits in 0x07 and just care about the global
// clipping mask.
// Otherwise, we only want to drawFill the antialiased parts that are
// *outside* the current shape. This is important in case the fill
// or stroke color is translucent. If we wouldn't clip to outside
// the current shape, some pixels from the outline stroke overlapped
// the (non-antialiased) fill.
const depthMode = painter.depthModeForSublayer(
layer.getPaintProperty('fill-outline-color') ? 2 : 0, DepthMode.ReadOnly);
drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, true);
}
}

function drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, isOutline) {
function drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, isOutline, drawToOffscreen) {
const gl = painter.context.gl;

const patternProperty = layer.paint.get('fill-pattern');
Expand All @@ -65,7 +99,11 @@ function drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode
let drawMode, programName, uniformValues, indexBuffer, segments;

if (!isOutline) {
programName = image ? 'fillPattern' : 'fill';
if (drawToOffscreen) {
programName = 'fillopaque';
} else {
programName = image ? 'fillPattern' : 'fill';
}
drawMode = gl.TRIANGLES;
} else {
programName = image && !layer.getPaintProperty('fill-outline-color') ? 'fillOutlinePattern' : 'fillOutline';
Expand Down Expand Up @@ -115,8 +153,40 @@ function drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode
}

program.draw(painter.context, drawMode, depthMode,
painter.stencilModeForClipping(coord), colorMode, CullFaceMode.disabled, uniformValues,
drawToOffscreen ? StencilMode.disabled : painter.stencilModeForClipping(coord), colorMode, CullFaceMode.disabled, uniformValues,
layer.id, bucket.layoutVertexBuffer, indexBuffer, segments,
layer.paint, painter.transform.zoom, programConfiguration);
}
}

function bindFramebuffer(context, painter, layer) {
const gl = context.gl;
context.activeTexture.set(gl.TEXTURE1);

context.viewport.set([0, 0, painter.width, painter.height]);

let fbo = layer.fillFbo;

if (!fbo) {
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);

fbo = layer.fillFbo = context.createFramebuffer(painter.width, painter.height, false);

bindTextureToFramebuffer(context, painter, texture, fbo);

} else {
gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
context.bindFramebuffer.set(fbo.framebuffer);
}
}

function bindTextureToFramebuffer(context, painter, texture, fbo) {
const gl = context.gl;
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, painter.width, painter.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
fbo.colorAttachment.set(texture);
}
28 changes: 28 additions & 0 deletions src/render/program/fill_program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ export type FillOutlineUniformsType = {
'u_world': Uniform2f;
};

export type FillfboUniformsType = {
'u_matrix': UniformMatrix4f;
'u_world': Uniform2f;
'u_image': Uniform1i;
'u_opacity': Uniform1f;
};

export type FillPatternUniformsType = {
'u_matrix': UniformMatrix4f;
// pattern uniforms:
Expand Down Expand Up @@ -51,6 +58,13 @@ const fillUniforms = (context: Context, locations: UniformLocations): FillUnifor
'u_matrix': new UniformMatrix4f(context, locations.u_matrix)
});

const fillfboUniforms = (context: Context, locations: UniformLocations): FillfboUniformsType => ({
'u_matrix': new UniformMatrix4f(context, locations.u_matrix),
'u_world': new Uniform2f(context, locations.u_world),
'u_image': new Uniform1i(context, locations.u_image),
'u_opacity': new Uniform1f(context, locations.u_opacity)
});

const fillPatternUniforms = (context: Context, locations: UniformLocations): FillPatternUniformsType => ({
'u_matrix': new UniformMatrix4f(context, locations.u_matrix),
'u_image': new Uniform1i(context, locations.u_image),
Expand All @@ -77,6 +91,18 @@ const fillOutlinePatternUniforms = (context: Context, locations: UniformLocation
'u_fade': new Uniform1f(context, locations.u_fade)
});

const fillfboUniformValues = (painter: Painter, textureUnit: number, opacity = 1.0): UniformValues<FillfboUniformsType> => {
const gl = painter.context.gl;
const matrix = mat4.create();
mat4.ortho(matrix, 0, painter.width, painter.height, 0, 0, 1);
return {
'u_matrix': matrix,
'u_world': [gl.drawingBufferWidth, gl.drawingBufferHeight],
'u_image': textureUnit,
'u_opacity': opacity
};
};

const fillUniformValues = (matrix: mat4): UniformValues<FillUniformsType> => ({
'u_matrix': matrix
});
Expand Down Expand Up @@ -117,5 +143,7 @@ export {
fillUniformValues,
fillPatternUniformValues,
fillOutlineUniformValues,
fillfboUniformValues,
fillfboUniforms,
fillOutlinePatternUniformValues
};
4 changes: 3 additions & 1 deletion src/render/program/program_uniforms.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {fillExtrusionUniforms, fillExtrusionPatternUniforms} from './fill_extrusion_program';
import {fillUniforms, fillPatternUniforms, fillOutlineUniforms, fillOutlinePatternUniforms} from './fill_program';
import {fillUniforms, fillPatternUniforms, fillOutlineUniforms, fillOutlinePatternUniforms, fillfboUniforms} from './fill_program';
import {circleUniforms} from './circle_program';
import {collisionUniforms, collisionCircleUniforms} from './collision_program';
import {debugUniforms} from './debug_program';
Expand All @@ -15,6 +15,8 @@ export const programUniforms = {
fillExtrusion: fillExtrusionUniforms,
fillExtrusionPattern: fillExtrusionPatternUniforms,
fill: fillUniforms,
fillopaque: fillUniforms,
fillfbo: fillfboUniforms,
fillPattern: fillPatternUniforms,
fillOutline: fillOutlineUniforms,
fillOutlinePattern: fillOutlinePatternUniforms,
Expand Down
12 changes: 12 additions & 0 deletions src/shaders/fillfbo.fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
uniform sampler2D u_image;
uniform float u_opacity;
varying vec2 v_pos;

void main() {
vec4 color = texture2D(u_image, v_pos);
gl_FragColor = color * u_opacity;

#ifdef OVERDRAW_INSPECTOR
gl_FragColor = vec4(1.0);
#endif
}
11 changes: 11 additions & 0 deletions src/shaders/fillfbo.vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
uniform mat4 u_matrix;
uniform vec2 u_world;
attribute vec2 a_pos;
varying vec2 v_pos;

void main() {
gl_Position = u_matrix * vec4(a_pos * u_world, 0, 1);

v_pos.x = a_pos.x;
v_pos.y = 1.0 - a_pos.y;
}
13 changes: 13 additions & 0 deletions src/shaders/fillopaque.fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma mapbox: define highp vec4 color
#pragma mapbox: define lowp float opacity

void main() {
#pragma mapbox: initialize highp vec4 color
#pragma mapbox: initialize lowp float opacity

gl_FragColor = color;

#ifdef OVERDRAW_INSPECTOR
gl_FragColor = vec4(1.0);
#endif
}
13 changes: 13 additions & 0 deletions src/shaders/fillopaque.vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
attribute vec2 a_pos;

uniform mat4 u_matrix;

#pragma mapbox: define highp vec4 color
#pragma mapbox: define lowp float opacity

void main() {
#pragma mapbox: initialize highp vec4 color
#pragma mapbox: initialize lowp float opacity

gl_Position = u_matrix * vec4(a_pos, 0, 1);
}
Loading

0 comments on commit f20692a

Please sign in to comment.