diff --git a/debug/threejs.html b/debug/threejs.html index cca332b4f4a..2587ec0348e 100644 --- a/debug/threejs.html +++ b/debug/threejs.html @@ -22,6 +22,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', + antialias: true, zoom: 16.5, center: [-79.390307, 43.658956], bearing: 20, diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index e9dd3328e2b..6b493edc2ca 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -4,7 +4,6 @@ export default drawCustom; import DepthMode from '../gl/depth_mode'; import StencilMode from '../gl/stencil_mode'; -import {prepareOffscreenFramebuffer, drawOffscreenTexture} from './offscreen'; import type Painter from './painter'; import type SourceCache from '../source/source_cache'; @@ -27,35 +26,23 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty painter.setBaseState(); } - if (implementation.renderingMode === '3d') { - painter.setCustomLayerDefaults(); - - prepareOffscreenFramebuffer(painter, layer); - implementation.render(context.gl, painter.transform.customLayerMatrix()); - - context.setDirty(); - painter.setBaseState(); - } - } else if (painter.renderPass === 'translucent') { - if (implementation.renderingMode === '3d') { - drawOffscreenTexture(painter, layer, 1); + painter.setCustomLayerDefaults(); - } else { - painter.setCustomLayerDefaults(); + context.setColorMode(painter.colorModeForRenderPass()); + context.setStencilMode(StencilMode.disabled); - context.setColorMode(painter.colorModeForRenderPass()); - context.setStencilMode(StencilMode.disabled); + const depthMode = implementation.renderingMode === '3d' ? + new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D) : + painter.depthModeForSublayer(0, DepthMode.ReadOnly); - const depthMode = painter.depthModeForSublayer(0, DepthMode.ReadOnly); - context.setDepthMode(depthMode); + context.setDepthMode(depthMode); - implementation.render(context.gl, painter.transform.customLayerMatrix()); + implementation.render(context.gl, painter.transform.customLayerMatrix()); - context.setDirty(); - painter.setBaseState(); - context.bindFramebuffer.set(null); - } + context.setDirty(); + painter.setBaseState(); + context.bindFramebuffer.set(null); } } diff --git a/src/render/draw_fill.js b/src/render/draw_fill.js index 9a55a8df978..43881296ab9 100644 --- a/src/render/draw_fill.js +++ b/src/render/draw_fill.js @@ -29,7 +29,8 @@ function drawFill(painter: Painter, sourceCache: SourceCache, layer: FillStyleLa const colorMode = painter.colorModeForRenderPass(); const pattern = layer.paint.get('fill-pattern'); - const pass = (!pattern.constantOr((1: any)) && + const pass = painter.opaquePassEnabledForLayer() && + (!pattern.constantOr((1: any)) && color.constantOr(Color.transparent).a === 1 && opacity.constantOr(0) === 1) ? 'opaque' : 'translucent'; diff --git a/src/render/draw_fill_extrusion.js b/src/render/draw_fill_extrusion.js index 40f1f03dac1..444f3d8d914 100644 --- a/src/render/draw_fill_extrusion.js +++ b/src/render/draw_fill_extrusion.js @@ -2,12 +2,12 @@ import DepthMode from '../gl/depth_mode'; import StencilMode from '../gl/stencil_mode'; +import ColorMode from '../gl/color_mode'; import CullFaceMode from '../gl/cull_face_mode'; import { fillExtrusionUniformValues, fillExtrusionPatternUniformValues, } from './program/fill_extrusion_program'; -import {prepareOffscreenFramebuffer, drawOffscreenTexture} from './offscreen'; import type Painter from './painter'; import type SourceCache from '../source/source_cache'; @@ -18,21 +18,32 @@ import type {OverscaledTileID} from '../source/tile_id'; export default draw; function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLayer, coords: Array) { - if (layer.paint.get('fill-extrusion-opacity') === 0) { + const opacity = layer.paint.get('fill-extrusion-opacity'); + if (opacity === 0) { return; } - if (painter.renderPass === 'offscreen') { - prepareOffscreenFramebuffer(painter, layer); - - const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, [0, 1]), - stencilMode = StencilMode.disabled, - colorMode = painter.colorModeForRenderPass(); - - drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMode, colorMode); - - } else if (painter.renderPass === 'translucent') { - drawOffscreenTexture(painter, layer, layer.paint.get('fill-extrusion-opacity')); + if (painter.renderPass === 'translucent') { + const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D); + + if (opacity === 1 && !layer.paint.get('fill-extrusion-pattern').constantOr((1: any))) { + const colorMode = painter.colorModeForRenderPass(); + drawExtrusionTiles(painter, source, layer, coords, depthMode, StencilMode.disabled, colorMode); + + } else { + // Draw transparent buildings in two passes so that only the closest surface is drawn. + // First draw all the extrusions into only the depth buffer. No colors are drawn. + drawExtrusionTiles(painter, source, layer, coords, depthMode, + StencilMode.disabled, + ColorMode.disabled); + + // Then draw all the extrusions a second type, only coloring fragments if they have the + // same depth value as the closest fragment in the previous pass. Use the stencil buffer + // to prevent the second draw in cases where we have coincident polygons. + drawExtrusionTiles(painter, source, layer, coords, depthMode, + painter.stencilModeFor3D(), + painter.colorModeForRenderPass()); + } } } @@ -42,6 +53,7 @@ function drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMo const patternProperty = layer.paint.get('fill-extrusion-pattern'); const image = patternProperty.constantOr((1: any)); const crossfade = layer.getCrossfadeParameters(); + const opacity = layer.paint.get('fill-extrusion-opacity'); for (const coord of coords) { const tile = source.getTile(coord); @@ -72,8 +84,8 @@ function drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMo const shouldUseVerticalGradient = layer.paint.get('fill-extrusion-vertical-gradient'); const uniformValues = image ? - fillExtrusionPatternUniformValues(matrix, painter, shouldUseVerticalGradient, coord, crossfade, tile) : - fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient); + fillExtrusionPatternUniformValues(matrix, painter, shouldUseVerticalGradient, opacity, coord, crossfade, tile) : + fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient, opacity); program.draw(context, context.gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, diff --git a/src/render/offscreen.js b/src/render/offscreen.js deleted file mode 100644 index 30e7bb1ec64..00000000000 --- a/src/render/offscreen.js +++ /dev/null @@ -1,64 +0,0 @@ -// @flow - -import Texture from './texture'; -import Color from '../style-spec/util/color'; -import DepthMode from '../gl/depth_mode'; -import StencilMode from '../gl/stencil_mode'; -import CullFaceMode from '../gl/cull_face_mode'; -import {extrusionTextureUniformValues} from './program/fill_extrusion_program'; - -import type Painter from './painter'; -import type CustomStyleLayer from '../style/style_layer/custom_style_layer'; -import type FillExtrusionStyleLayer from '../style/style_layer/fill_extrusion_style_layer'; - -export function prepareOffscreenFramebuffer(painter: Painter, layer: CustomStyleLayer | FillExtrusionStyleLayer) { - const context = painter.context; - const gl = context.gl; - - let renderTarget = layer.viewportFrame; - - if (painter.depthRboNeedsClear) { - painter.setupOffscreenDepthRenderbuffer(); - } - - if (!renderTarget) { - const texture = new Texture(context, {width: painter.width, height: painter.height, data: null}, gl.RGBA); - texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); - - renderTarget = layer.viewportFrame = context.createFramebuffer(painter.width, painter.height); - renderTarget.colorAttachment.set(texture.texture); - } - - context.bindFramebuffer.set(renderTarget.framebuffer); - renderTarget.depthAttachment.set(painter.depthRbo); - - if (painter.depthRboNeedsClear) { - context.clear({ depth: 1 }); - painter.depthRboNeedsClear = false; - } - - context.clear({ color: Color.transparent }); - - context.setStencilMode(StencilMode.disabled); - context.setDepthMode(new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1])); - context.setColorMode(painter.colorModeForRenderPass()); -} - -export function drawOffscreenTexture(painter: Painter, layer: CustomStyleLayer | FillExtrusionStyleLayer, opacity: number) { - const renderedTexture = layer.viewportFrame; - if (!renderedTexture) return; - - const context = painter.context; - const gl = context.gl; - - context.activeTexture.set(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, renderedTexture.colorAttachment.get()); - - painter.useProgram('extrusionTexture').draw(context, gl.TRIANGLES, - DepthMode.disabled, StencilMode.disabled, - painter.colorModeForRenderPass(), - CullFaceMode.disabled, - extrusionTextureUniformValues(painter, opacity, 0), - layer.id, painter.viewportBuffer, painter.quadTriangleIndexBuffer, - painter.viewportSegments, layer.paint, painter.transform.zoom); -} diff --git a/src/render/painter.js b/src/render/painter.js index 6ccd0ba4743..03cadf9e8ba 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -61,7 +61,7 @@ import type ImageManager from './image_manager'; import type GlyphManager from './glyph_manager'; import type VertexBuffer from '../gl/vertex_buffer'; import type IndexBuffer from '../gl/index_buffer'; -import type {DepthMaskType, DepthFuncType} from '../gl/types'; +import type {DepthRangeType, DepthMaskType, DepthFuncType} from '../gl/types'; export type RenderPass = 'offscreen' | 'opaque' | 'translucent'; @@ -108,9 +108,12 @@ class Painter { lineAtlas: LineAtlas; imageManager: ImageManager; glyphManager: GlyphManager; - depthRange: number; + depthRangeFor3D: DepthRangeType; + opaquePassCutoff: number; renderPass: RenderPass; currentLayer: number; + currentStencilSource: ?string; + nextStencilID: number; id: string; _showOverdrawInspector: boolean; cache: { [string]: Program<*> }; @@ -219,6 +222,9 @@ class Painter { const context = this.context; const gl = context.gl; + this.nextStencilID = 1; + this.currentStencilSource = undefined; + // As a temporary workaround for https://github.com/mapbox/mapbox-gl-js/issues/5490, // pending an upstream fix, we draw a fullscreen stencil=0 clipping mask here, // effectively clearing the stencil buffer: once an upstream patch lands, remove @@ -235,20 +241,28 @@ class Painter { this.quadTriangleIndexBuffer, this.viewportSegments); } - _renderTileClippingMasks(tileIDs: Array) { + _renderTileClippingMasks(layer: StyleLayer, tileIDs: Array) { + if (this.currentStencilSource === layer.source || !layer.isTileClipped() || !tileIDs || !tileIDs.length) return; + + this.currentStencilSource = layer.source; + const context = this.context; const gl = context.gl; + if (this.nextStencilID + tileIDs.length > 256) { + // we'll run out of fresh IDs so we need to clear and start from scratch + this.clearStencil(); + } + context.setColorMode(ColorMode.disabled); context.setDepthMode(DepthMode.disabled); const program = this.useProgram('clippingMask'); - let idNext = 1; this._tileClippingMaskIDs = {}; for (const tileID of tileIDs) { - const id = this._tileClippingMaskIDs[tileID.key] = idNext++; + const id = this._tileClippingMaskIDs[tileID.key] = this.nextStencilID++; program.draw(context, gl.TRIANGLES, DepthMode.disabled, // Tests will always pass, and ref value will be written to stencil buffer. @@ -259,6 +273,16 @@ class Painter { } } + stencilModeFor3D(): StencilMode { + if (this.nextStencilID + 1 > 256) { + this.clearStencil(); + } + + const id = this.nextStencilID++; + const gl = this.context.gl; + return new StencilMode({ func: gl.NOTEQUAL, mask: 0xFF }, id, 0xFF, gl.KEEP, gl.KEEP, gl.REPLACE); + } + stencilModeForClipping(tileID: OverscaledTileID): StencilMode { const gl = this.context.gl; return new StencilMode({ func: gl.EQUAL, mask: 0xFF }, this._tileClippingMaskIDs[tileID.key], 0x00, gl.KEEP, gl.KEEP, gl.REPLACE); @@ -278,11 +302,23 @@ class Painter { } } - depthModeForSublayer(n: number, mask: DepthMaskType, func: ?DepthFuncType): DepthMode { + depthModeForSublayer(n: number, mask: DepthMaskType, func: ?DepthFuncType): $ReadOnly { + if (!this.opaquePassEnabledForLayer()) return DepthMode.disabled; const depth = 1 - ((1 + this.currentLayer) * this.numSublayers + n) * this.depthEpsilon; return new DepthMode(func || this.context.gl.LEQUAL, mask, [depth, depth]); } + /* + * The opaque pass and 3D layers both use the depth buffer. + * Layers drawn above 3D layers need to be drawn using the + * painter's algorithm so that they appear above 3D features. + * This returns true for layers that can be drawn using the + * opaque pass. + */ + opaquePassEnabledForLayer() { + return this.currentLayer < this.opaquePassCutoff; + } + render(style: Style, options: PainterOptions) { this.style = style; this.options = options; @@ -323,6 +359,15 @@ class Painter { updateTileMasks(visibleTiles, this.context); } + this.opaquePassCutoff = Infinity; + for (let i = 0; i < layerIds.length; i++) { + const layerId = layerIds[i]; + if (this.style._layers[layerId].is3D()) { + this.opaquePassCutoff = i; + break; + } + } + // Offscreen pass =============================================== // We first do all rendering that requires rendering to a separate // framebuffer, and then save those for rendering back to the map @@ -345,36 +390,29 @@ class Painter { // Clear buffers in preparation for drawing to the main framebuffer this.context.clear({ color: options.showOverdrawInspector ? Color.black : Color.transparent, depth: 1 }); + this.clearStencil(); this._showOverdrawInspector = options.showOverdrawInspector; - this.depthRange = (style._order.length + 2) * this.numSublayers * this.depthEpsilon; + this.depthRangeFor3D = [0, 1 - ((style._order.length + 2) * this.numSublayers * this.depthEpsilon)]; // Opaque pass =============================================== // Draw opaque layers top-to-bottom first. this.renderPass = 'opaque'; - let prevSourceId; for (this.currentLayer = layerIds.length - 1; this.currentLayer >= 0; this.currentLayer--) { const layer = this.style._layers[layerIds[this.currentLayer]]; const sourceCache = sourceCaches[layer.source]; const coords = coordsAscending[layer.source]; - if (layer.source !== prevSourceId && sourceCache) { - this.clearStencil(); - if (sourceCache.getSource().isTileClipped) { - this._renderTileClippingMasks(coords); - } - } - + this._renderTileClippingMasks(layer, coords); this.renderLayer(this, sourceCache, layer, coords); - prevSourceId = layer.source; } // Translucent pass =============================================== // Draw all other layers bottom-to-top. this.renderPass = 'translucent'; - for (this.currentLayer = 0, prevSourceId = null; this.currentLayer < layerIds.length; this.currentLayer++) { + for (this.currentLayer = 0; this.currentLayer < layerIds.length; this.currentLayer++) { const layer = this.style._layers[layerIds[this.currentLayer]]; const sourceCache = sourceCaches[layer.source]; @@ -383,15 +421,8 @@ class Painter { // separate clipping masks const coords = (layer.type === 'symbol' ? coordsDescendingSymbol : coordsDescending)[layer.source]; - if (layer.source !== prevSourceId && sourceCache) { - this.clearStencil(); - if (sourceCache.getSource().isTileClipped) { - this._renderTileClippingMasks(coordsAscending[layer.source]); - } - } - + this._renderTileClippingMasks(layer, coordsAscending[layer.source]); this.renderLayer(this, sourceCache, layer, coords); - prevSourceId = layer.source; } if (this.options.showTileBoundaries) { diff --git a/src/render/program/fill_extrusion_program.js b/src/render/program/fill_extrusion_program.js index 080420a9b37..dbf4a680d7a 100644 --- a/src/render/program/fill_extrusion_program.js +++ b/src/render/program/fill_extrusion_program.js @@ -10,7 +10,7 @@ import { UniformMatrix4f } from '../uniform_binding'; -import {mat3, vec3, mat4} from 'gl-matrix'; +import {mat3, vec3} from 'gl-matrix'; import { extend } from '../../util/util'; import type Context from '../../gl/context'; @@ -25,7 +25,8 @@ export type FillExtrusionUniformsType = {| 'u_lightpos': Uniform3f, 'u_lightintensity': Uniform1f, 'u_lightcolor': Uniform3f, - 'u_vertical_gradient': Uniform1f + 'u_vertical_gradient': Uniform1f, + 'u_opacity': Uniform1f |}; export type FillExtrusionPatternUniformsType = {| @@ -41,13 +42,7 @@ export type FillExtrusionPatternUniformsType = {| 'u_pixel_coord_upper': Uniform2f, 'u_pixel_coord_lower': Uniform2f, 'u_scale': Uniform4f, - 'u_fade': Uniform1f -|}; - -export type ExtrusionTextureUniformsType = {| - 'u_matrix': UniformMatrix4f, - 'u_world': Uniform2f, - 'u_image': Uniform1i, + 'u_fade': Uniform1f, 'u_opacity': Uniform1f |}; @@ -56,7 +51,8 @@ const fillExtrusionUniforms = (context: Context, locations: UniformLocations): F 'u_lightpos': new Uniform3f(context, locations.u_lightpos), 'u_lightintensity': new Uniform1f(context, locations.u_lightintensity), 'u_lightcolor': new Uniform3f(context, locations.u_lightcolor), - 'u_vertical_gradient': new Uniform1f(context, locations.u_vertical_gradient) + 'u_vertical_gradient': new Uniform1f(context, locations.u_vertical_gradient), + 'u_opacity': new Uniform1f(context, locations.u_opacity) }); const fillExtrusionPatternUniforms = (context: Context, locations: UniformLocations): FillExtrusionPatternUniformsType => ({ @@ -72,20 +68,15 @@ const fillExtrusionPatternUniforms = (context: Context, locations: UniformLocati 'u_pixel_coord_upper': new Uniform2f(context, locations.u_pixel_coord_upper), 'u_pixel_coord_lower': new Uniform2f(context, locations.u_pixel_coord_lower), 'u_scale': new Uniform4f(context, locations.u_scale), - 'u_fade': new Uniform1f(context, locations.u_fade) -}); - -const extrusionTextureUniforms = (context: Context, locations: UniformLocations): ExtrusionTextureUniformsType => ({ - '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_fade': new Uniform1f(context, locations.u_fade), 'u_opacity': new Uniform1f(context, locations.u_opacity) }); const fillExtrusionUniformValues = ( matrix: Float32Array, painter: Painter, - shouldUseVerticalGradient: boolean + shouldUseVerticalGradient: boolean, + opacity: number ): UniformValues => { const light = painter.style.light; const _lp = light.properties.get('position'); @@ -103,7 +94,8 @@ const fillExtrusionUniformValues = ( 'u_lightpos': lightPos, 'u_lightintensity': light.properties.get('intensity'), 'u_lightcolor': [lightColor.r, lightColor.g, lightColor.b], - 'u_vertical_gradient': +shouldUseVerticalGradient + 'u_vertical_gradient': +shouldUseVerticalGradient, + 'u_opacity': opacity }; }; @@ -111,40 +103,21 @@ const fillExtrusionPatternUniformValues = ( matrix: Float32Array, painter: Painter, shouldUseVerticalGradient: boolean, + opacity: number, coord: OverscaledTileID, crossfade: CrossfadeParameters, tile: Tile ): UniformValues => { - return extend(fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient), + return extend(fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient, opacity), patternUniformValues(crossfade, painter, tile), { 'u_height_factor': -Math.pow(2, coord.overscaledZ) / tile.tileSize / 8 }); }; -const extrusionTextureUniformValues = ( - painter: Painter, - opacity: number, - textureUnit: number -): UniformValues => { - const matrix = mat4.create(); - mat4.ortho(matrix, 0, painter.width, painter.height, 0, 0, 1); - - const gl = painter.context.gl; - - return { - 'u_matrix': matrix, - 'u_world': [gl.drawingBufferWidth, gl.drawingBufferHeight], - 'u_image': textureUnit, - 'u_opacity': opacity - }; -}; - export { fillExtrusionUniforms, fillExtrusionPatternUniforms, - extrusionTextureUniforms, fillExtrusionUniformValues, - fillExtrusionPatternUniformValues, - extrusionTextureUniformValues + fillExtrusionPatternUniformValues }; diff --git a/src/render/program/program_uniforms.js b/src/render/program/program_uniforms.js index 5dbf64cef07..34fafaa4993 100644 --- a/src/render/program/program_uniforms.js +++ b/src/render/program/program_uniforms.js @@ -1,6 +1,6 @@ // @flow -import { fillExtrusionUniforms, fillExtrusionPatternUniforms, extrusionTextureUniforms } from './fill_extrusion_program'; +import { fillExtrusionUniforms, fillExtrusionPatternUniforms } from './fill_extrusion_program'; import { fillUniforms, fillPatternUniforms, fillOutlineUniforms, fillOutlinePatternUniforms } from './fill_program'; import { circleUniforms } from './circle_program'; import { collisionUniforms } from './collision_program'; @@ -16,7 +16,6 @@ import { backgroundUniforms, backgroundPatternUniforms } from './background_prog export const programUniforms = { fillExtrusion: fillExtrusionUniforms, fillExtrusionPattern: fillExtrusionPatternUniforms, - extrusionTexture: extrusionTextureUniforms, fill: fillUniforms, fillPattern: fillPatternUniforms, fillOutline: fillOutlineUniforms, diff --git a/src/shaders/extrusion_texture.fragment.glsl b/src/shaders/extrusion_texture.fragment.glsl deleted file mode 100644 index d857c50b894..00000000000 --- a/src/shaders/extrusion_texture.fragment.glsl +++ /dev/null @@ -1,11 +0,0 @@ -uniform sampler2D u_image; -uniform float u_opacity; -varying vec2 v_pos; - -void main() { - gl_FragColor = texture2D(u_image, v_pos) * u_opacity; - -#ifdef OVERDRAW_INSPECTOR - gl_FragColor = vec4(0.0); -#endif -} diff --git a/src/shaders/extrusion_texture.vertex.glsl b/src/shaders/extrusion_texture.vertex.glsl deleted file mode 100644 index 0e57d0faaf7..00000000000 --- a/src/shaders/extrusion_texture.vertex.glsl +++ /dev/null @@ -1,11 +0,0 @@ -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; -} diff --git a/src/shaders/fill_extrusion.vertex.glsl b/src/shaders/fill_extrusion.vertex.glsl index df1b8556227..7a771b6dc97 100644 --- a/src/shaders/fill_extrusion.vertex.glsl +++ b/src/shaders/fill_extrusion.vertex.glsl @@ -3,6 +3,7 @@ uniform vec3 u_lightcolor; uniform lowp vec3 u_lightpos; uniform lowp float u_lightintensity; uniform float u_vertical_gradient; +uniform lowp float u_opacity; attribute vec2 a_pos; attribute vec4 a_normal_ed; @@ -61,4 +62,5 @@ void main() { v_color.r += clamp(color.r * directional * u_lightcolor.r, mix(0.0, 0.3, 1.0 - u_lightcolor.r), 1.0); v_color.g += clamp(color.g * directional * u_lightcolor.g, mix(0.0, 0.3, 1.0 - u_lightcolor.g), 1.0); v_color.b += clamp(color.b * directional * u_lightcolor.b, mix(0.0, 0.3, 1.0 - u_lightcolor.b), 1.0); + v_color *= u_opacity; } diff --git a/src/shaders/fill_extrusion_pattern.vertex.glsl b/src/shaders/fill_extrusion_pattern.vertex.glsl index 4c314f19bff..2761d56b6a9 100644 --- a/src/shaders/fill_extrusion_pattern.vertex.glsl +++ b/src/shaders/fill_extrusion_pattern.vertex.glsl @@ -4,6 +4,7 @@ uniform vec2 u_pixel_coord_lower; uniform float u_height_factor; uniform vec4 u_scale; uniform float u_vertical_gradient; +uniform lowp float u_opacity; uniform vec3 u_lightcolor; uniform lowp vec3 u_lightpos; @@ -71,4 +72,5 @@ void main() { } v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0)); + v_lighting *= u_opacity; } diff --git a/src/shaders/shaders.js b/src/shaders/shaders.js index 7c569fda562..85c9e058349 100644 --- a/src/shaders/shaders.js +++ b/src/shaders/shaders.js @@ -34,8 +34,6 @@ import fillExtrusionFrag from './fill_extrusion.fragment.glsl'; import fillExtrusionVert from './fill_extrusion.vertex.glsl'; import fillExtrusionPatternFrag from './fill_extrusion_pattern.fragment.glsl'; import fillExtrusionPatternVert from './fill_extrusion_pattern.vertex.glsl'; -import extrusionTextureFrag from './extrusion_texture.fragment.glsl'; -import extrusionTextureVert from './extrusion_texture.vertex.glsl'; import hillshadePrepareFrag from './hillshade_prepare.fragment.glsl'; import hillshadePrepareVert from './hillshade_prepare.vertex.glsl'; import hillshadeFrag from './hillshade.fragment.glsl'; @@ -71,7 +69,6 @@ export const fillOutlinePattern = compile(fillOutlinePatternFrag, fillOutlinePat export const fillPattern = compile(fillPatternFrag, fillPatternVert); export const fillExtrusion = compile(fillExtrusionFrag, fillExtrusionVert); export const fillExtrusionPattern = compile(fillExtrusionPatternFrag, fillExtrusionPatternVert); -export const extrusionTexture = compile(extrusionTextureFrag, extrusionTextureVert); export const hillshadePrepare = compile(hillshadePrepareFrag, hillshadePrepareVert); export const hillshade = compile(hillshadeFrag, hillshadeVert); export const line = compile(lineFrag, lineVert); diff --git a/src/style/style_layer.js b/src/style/style_layer.js index 131f055aedf..263b123eab7 100644 --- a/src/style/style_layer.js +++ b/src/style/style_layer.js @@ -237,6 +237,14 @@ class StyleLayer extends Evented { })); } + is3D() { + return false; + } + + isTileClipped() { + return false; + } + hasOffscreenPass() { return false; } diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index ebb88deedb5..9e585ff8a44 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -1,7 +1,6 @@ // @flow import StyleLayer from '../style_layer'; -import type Framebuffer from '../../gl/framebuffer'; import type Map from '../../ui/map'; import assert from 'assert'; @@ -175,16 +174,18 @@ export function validateCustomStyleLayer(layerObject: CustomLayerInterface) { class CustomStyleLayer extends StyleLayer { implementation: CustomLayerInterface; - viewportFrame: ?Framebuffer; constructor(implementation: CustomLayerInterface) { super(implementation, {}); this.implementation = implementation; } + is3D() { + return this.implementation.renderingMode === '3d'; + } hasOffscreenPass() { - return this.implementation.prerender !== undefined || this.implementation.renderingMode === '3d'; + return this.implementation.prerender !== undefined; } recalculate() {} @@ -195,13 +196,6 @@ class CustomStyleLayer extends StyleLayer { assert(false, "Custom layers cannot be serialized"); } - resize() { - if (this.viewportFrame) { - this.viewportFrame.destroy(); - this.viewportFrame = null; - } - } - onAdd(map: Map) { if (this.implementation.onAdd) { this.implementation.onAdd(map, map.painter.context.gl); diff --git a/src/style/style_layer/fill_extrusion_style_layer.js b/src/style/style_layer/fill_extrusion_style_layer.js index 1bfb784d641..9e90893e7b3 100644 --- a/src/style/style_layer/fill_extrusion_style_layer.js +++ b/src/style/style_layer/fill_extrusion_style_layer.js @@ -13,7 +13,6 @@ import Point from '@mapbox/point-geometry'; import type { FeatureState } from '../../style-spec/expression'; import type {BucketParameters} from '../../data/bucket'; import type {PaintProps} from './fill_extrusion_style_layer_properties'; -import type Framebuffer from '../../gl/framebuffer'; import type Transform from '../../geo/transform'; import type {LayerSpecification} from '../../style-spec/types'; @@ -21,7 +20,6 @@ class FillExtrusionStyleLayer extends StyleLayer { _transitionablePaint: Transitionable; _transitioningPaint: Transitioning; paint: PossiblyEvaluated; - viewportFrame: ?Framebuffer; constructor(layer: LayerSpecification) { super(layer, properties); @@ -35,6 +33,10 @@ class FillExtrusionStyleLayer extends StyleLayer { return translateDistance(this.paint.get('fill-extrusion-translate')); } + is3D(): boolean { + return true; + } + queryIntersectsFeature(queryGeometry: Array, feature: VectorTileFeature, featureState: FeatureState, @@ -59,17 +61,6 @@ class FillExtrusionStyleLayer extends StyleLayer { const projectedTop = projected[1]; return checkIntersection(projectedBase, projectedTop, projectedQueryGeometry); } - - hasOffscreenPass() { - return this.paint.get('fill-extrusion-opacity') !== 0 && this.visibility !== 'none'; - } - - resize() { - if (this.viewportFrame) { - this.viewportFrame.destroy(); - this.viewportFrame = null; - } - } } function dot(a, b) { diff --git a/src/style/style_layer/fill_style_layer.js b/src/style/style_layer/fill_style_layer.js index 9d46e9cad1b..c7a555676d5 100644 --- a/src/style/style_layer/fill_style_layer.js +++ b/src/style/style_layer/fill_style_layer.js @@ -55,6 +55,10 @@ class FillStyleLayer extends StyleLayer { transform.angle, pixelsToTileUnits); return polygonIntersectsMultiPolygon(translatedPolygon, geometry); } + + isTileClipped() { + return true; + } } export default FillStyleLayer; diff --git a/src/style/style_layer/line_style_layer.js b/src/style/style_layer/line_style_layer.js index 224836e4ebb..63ce50f1c4b 100644 --- a/src/style/style_layer/line_style_layer.js +++ b/src/style/style_layer/line_style_layer.js @@ -110,6 +110,10 @@ class LineStyleLayer extends StyleLayer { } return polygonIntersectsBufferedMultiLine(translatedPolygon, geometry, halfWidth); } + + isTileClipped() { + return true; + } } export default LineStyleLayer; diff --git a/src/ui/map.js b/src/ui/map.js index ca86320323e..55a159fbf5f 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -75,6 +75,7 @@ type MapOptions = { logoPosition?: ControlPosition, failIfMajorPerformanceCaveat?: boolean, preserveDrawingBuffer?: boolean, + antialias?: boolean, refreshExpiredTiles?: boolean, maxBounds?: LngLatBoundsLike, scrollZoom?: boolean, @@ -183,6 +184,7 @@ const defaultOptions = { * @param {boolean} [options.failIfMajorPerformanceCaveat=false] If `true`, map creation will fail if the performance of Mapbox * GL JS would be dramatically worse than expected (i.e. a software renderer would be used). * @param {boolean} [options.preserveDrawingBuffer=false] If `true`, the map's canvas can be exported to a PNG using `map.getCanvas().toDataURL()`. This is `false` by default as a performance optimization. + * @param {boolean} [options.antialias] If `true`, create the gl context will be created with msaa antialiasing, which can be useful for antialiasing custom layers. this is `false` by default as a performance optimization. * @param {boolean} [options.refreshExpiredTiles=true] If `false`, the map won't attempt to re-request tiles once they expire per their HTTP `cacheControl`/`expires` headers. * @param {LngLatBoundsLike} [options.maxBounds] If set, the map will be constrained to the given bounds. * @param {boolean|Object} [options.scrollZoom=true] If `true`, the "scroll to zoom" interaction is enabled. An `Object` value is passed as options to {@link ScrollZoomHandler#enable}. @@ -255,6 +257,7 @@ class Map extends Camera { _trackResize: boolean; _preserveDrawingBuffer: boolean; _failIfMajorPerformanceCaveat: boolean; + _antialias: boolean; _refreshExpiredTiles: boolean; _hash: Hash; _delegatedListeners: any; @@ -317,6 +320,7 @@ class Map extends Camera { this._maxTileCacheSize = options.maxTileCacheSize; this._failIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat; this._preserveDrawingBuffer = options.preserveDrawingBuffer; + this._antialias = options.antialias; this._trackResize = options.trackResize; this._bearingSnap = options.bearingSnap; this._refreshExpiredTiles = options.refreshExpiredTiles; @@ -1547,10 +1551,11 @@ class Map extends Camera { } _setupPainter() { - const attributes = extend({ + const attributes = extend({}, isSupported.webGLContextAttributes, { failIfMajorPerformanceCaveat: this._failIfMajorPerformanceCaveat, - preserveDrawingBuffer: this._preserveDrawingBuffer - }, isSupported.webGLContextAttributes); + preserveDrawingBuffer: this._preserveDrawingBuffer, + antialias: this._antialias || false + }); const gl = this._canvas.getContext('webgl', attributes) || this._canvas.getContext('experimental-webgl', attributes); diff --git a/test/ignores.json b/test/ignores.json index ab5dfad74d0..57763daab8d 100644 --- a/test/ignores.json +++ b/test/ignores.json @@ -12,5 +12,6 @@ "render-tests/regressions/mapbox-gl-js#3682": "skip - true", "render-tests/runtime-styling/image-update-icon": "skip - https://github.com/mapbox/mapbox-gl-js/issues/4804", "render-tests/runtime-styling/image-update-pattern": "skip - https://github.com/mapbox/mapbox-gl-js/issues/4804", - "render-tests/mixed-zoom/z10-z11": "current behavior conflicts with https://github.com/mapbox/mapbox-gl-js/pull/6803. can be fixed when https://github.com/mapbox/api-maps/issues/1480 is done" + "render-tests/mixed-zoom/z10-z11": "current behavior conflicts with https://github.com/mapbox/mapbox-gl-js/pull/6803. can be fixed when https://github.com/mapbox/api-maps/issues/1480 is done", + "render-tests/fill-extrusion-pattern/tile-buffer": "https://github.com/mapbox/mapbox-gl-js/issues/4403" } diff --git a/test/integration/render-tests/custom-layer-js/tent-3d/expected.png b/test/integration/render-tests/custom-layer-js/tent-3d/expected.png index c6ee90d6690..cae223d5c02 100644 Binary files a/test/integration/render-tests/custom-layer-js/tent-3d/expected.png and b/test/integration/render-tests/custom-layer-js/tent-3d/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/@2x/expected.png b/test/integration/render-tests/fill-extrusion-pattern/@2x/expected.png index c7bdf3b886e..2994ade87fc 100644 Binary files a/test/integration/render-tests/fill-extrusion-pattern/@2x/expected.png and b/test/integration/render-tests/fill-extrusion-pattern/@2x/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/@2x/style.json b/test/integration/render-tests/fill-extrusion-pattern/@2x/style.json index d68b7d32b03..91ff69322a3 100644 --- a/test/integration/render-tests/fill-extrusion-pattern/@2x/style.json +++ b/test/integration/render-tests/fill-extrusion-pattern/@2x/style.json @@ -9,6 +9,7 @@ "sources": { "geojson": { "type": "geojson", + "buffer": 0, "data": { "type": "FeatureCollection", "features": [ @@ -96,4 +97,4 @@ } } ] -} \ No newline at end of file +} diff --git a/test/integration/render-tests/fill-extrusion-pattern/feature-expression/expected.png b/test/integration/render-tests/fill-extrusion-pattern/feature-expression/expected.png index aa2ef97b9d5..d7573106f76 100644 Binary files a/test/integration/render-tests/fill-extrusion-pattern/feature-expression/expected.png and b/test/integration/render-tests/fill-extrusion-pattern/feature-expression/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/feature-expression/style.json b/test/integration/render-tests/fill-extrusion-pattern/feature-expression/style.json index 360934bf306..8e408e81f55 100644 --- a/test/integration/render-tests/fill-extrusion-pattern/feature-expression/style.json +++ b/test/integration/render-tests/fill-extrusion-pattern/feature-expression/style.json @@ -8,6 +8,7 @@ "sources": { "geojson": { "type": "geojson", + "buffer": 0, "data": { "type": "FeatureCollection", "features": [ diff --git a/test/integration/render-tests/fill-extrusion-pattern/function-2/expected.png b/test/integration/render-tests/fill-extrusion-pattern/function-2/expected.png index 367f73f7545..cd86e8dccfe 100644 Binary files a/test/integration/render-tests/fill-extrusion-pattern/function-2/expected.png and b/test/integration/render-tests/fill-extrusion-pattern/function-2/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/function-2/style.json b/test/integration/render-tests/fill-extrusion-pattern/function-2/style.json index 3b51dc820f6..fcde2e7bab0 100644 --- a/test/integration/render-tests/fill-extrusion-pattern/function-2/style.json +++ b/test/integration/render-tests/fill-extrusion-pattern/function-2/style.json @@ -9,6 +9,7 @@ "sources": { "geojson": { "type": "geojson", + "buffer": 0, "data": { "type": "FeatureCollection", "features": [ diff --git a/test/integration/render-tests/fill-extrusion-pattern/function/expected.png b/test/integration/render-tests/fill-extrusion-pattern/function/expected.png index 753a82851c4..e60cd56c3a6 100644 Binary files a/test/integration/render-tests/fill-extrusion-pattern/function/expected.png and b/test/integration/render-tests/fill-extrusion-pattern/function/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/function/style.json b/test/integration/render-tests/fill-extrusion-pattern/function/style.json index 9849b8ae27a..8ff57ee9568 100644 --- a/test/integration/render-tests/fill-extrusion-pattern/function/style.json +++ b/test/integration/render-tests/fill-extrusion-pattern/function/style.json @@ -8,6 +8,7 @@ "sources": { "geojson": { "type": "geojson", + "buffer": 0, "data": { "type": "FeatureCollection", "features": [ @@ -107,4 +108,4 @@ } } ] -} \ No newline at end of file +} diff --git a/test/integration/render-tests/fill-extrusion-pattern/literal/expected.png b/test/integration/render-tests/fill-extrusion-pattern/literal/expected.png index 753a82851c4..8f4c7eb60d7 100644 Binary files a/test/integration/render-tests/fill-extrusion-pattern/literal/expected.png and b/test/integration/render-tests/fill-extrusion-pattern/literal/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/literal/style.json b/test/integration/render-tests/fill-extrusion-pattern/literal/style.json index 48abec51f93..39209caf993 100644 --- a/test/integration/render-tests/fill-extrusion-pattern/literal/style.json +++ b/test/integration/render-tests/fill-extrusion-pattern/literal/style.json @@ -8,6 +8,7 @@ "sources": { "geojson": { "type": "geojson", + "buffer": 0, "data": { "type": "FeatureCollection", "features": [ @@ -95,4 +96,4 @@ } } ] -} \ No newline at end of file +} diff --git a/test/integration/render-tests/fill-extrusion-pattern/opacity/expected.png b/test/integration/render-tests/fill-extrusion-pattern/opacity/expected.png index 09f7eb3e18d..31f8fb76e34 100644 Binary files a/test/integration/render-tests/fill-extrusion-pattern/opacity/expected.png and b/test/integration/render-tests/fill-extrusion-pattern/opacity/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/opacity/style.json b/test/integration/render-tests/fill-extrusion-pattern/opacity/style.json index f8694f44428..f0435da36db 100644 --- a/test/integration/render-tests/fill-extrusion-pattern/opacity/style.json +++ b/test/integration/render-tests/fill-extrusion-pattern/opacity/style.json @@ -8,6 +8,7 @@ "sources": { "geojson": { "type": "geojson", + "buffer": 0, "data": { "type": "FeatureCollection", "features": [ @@ -96,4 +97,4 @@ } } ] -} \ No newline at end of file +} diff --git a/test/integration/render-tests/fill-extrusion-pattern/tile-buffer/expected.png b/test/integration/render-tests/fill-extrusion-pattern/tile-buffer/expected.png new file mode 100644 index 00000000000..8b08b54ea97 Binary files /dev/null and b/test/integration/render-tests/fill-extrusion-pattern/tile-buffer/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-pattern/tile-buffer/style.json b/test/integration/render-tests/fill-extrusion-pattern/tile-buffer/style.json new file mode 100644 index 00000000000..dd60969401c --- /dev/null +++ b/test/integration/render-tests/fill-extrusion-pattern/tile-buffer/style.json @@ -0,0 +1,98 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256 + } + }, + "sources": { + "geojson": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "property": 20 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -0.0004, + 0 + ], + [ + -0.0002, + 0.0002 + ], + [ + 0, + 0 + ], + [ + -0.0002, + -0.0002 + ], + [ + -0.0004, + 0 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "property": 20 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + -0.0002 + ], + [ + 0, + 0.0002 + ], + [ + 0.0003, + 0.0002 + ], + [ + 0.0003, + -0.0002 + ], + [ + 0, + -0.0002 + ] + ] + ] + } + } + ] + } + } + }, + "sprite": "local://sprites/emerald", + "pitch": 60, + "zoom": 18, + "layers": [ + { + "id": "extrusion", + "type": "fill-extrusion", + "source": "geojson", + "paint": { + "fill-extrusion-pattern": "generic_icon", + "fill-extrusion-height": 10 + } + } + ] +}