diff --git a/src/ol/PluggableMap.js b/src/ol/PluggableMap.js index 6e3e4f797f..d671e9cad8 100644 --- a/src/ol/PluggableMap.js +++ b/src/ol/PluggableMap.js @@ -62,6 +62,7 @@ import {removeNode} from './dom.js'; * @property {Array} viewHints ViewHints. * @property {!Object>} wantedTiles WantedTiles. * @property {string} mapId The id of the map. + * @property {Object} renderTargets Identifiers of previously rendered elements. */ /** @@ -1477,6 +1478,7 @@ class PluggableMap extends BaseObject { viewHints: viewHints, wantedTiles: {}, mapId: getUid(this), + renderTargets: {}, }; if (viewState.nextCenter && viewState.nextResolution) { const rotation = isNaN(viewState.nextRotation) diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index 168eeaa9d6..0ee8129504 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -648,6 +648,7 @@ class RasterSource extends ImageSource { viewHints: [], wantedTiles: {}, mapId: getUid(this), + renderTargets: {}, }; this.setAttributions(function (frameState) { diff --git a/src/ol/webgl.js b/src/ol/webgl.js index 2e9ace2315..5479823cd1 100644 --- a/src/ol/webgl.js +++ b/src/ol/webgl.js @@ -2,6 +2,8 @@ * @module ol/webgl */ +import {assign} from './obj.js'; + /** * Constants taken from goog.webgl */ @@ -89,10 +91,11 @@ const CONTEXT_IDS = ['experimental-webgl', 'webgl', 'webkit-3d', 'moz-webgl']; * @return {WebGLRenderingContext} WebGL rendering context. */ export function getContext(canvas, opt_attributes) { + const attributes = assign({preserveDrawingBuffer: true}, opt_attributes); const ii = CONTEXT_IDS.length; for (let i = 0; i < ii; ++i) { try { - const context = canvas.getContext(CONTEXT_IDS[i], opt_attributes); + const context = canvas.getContext(CONTEXT_IDS[i], attributes); if (context) { return /** @type {!WebGLRenderingContext} */ (context); } diff --git a/src/ol/webgl/PostProcessingPass.js b/src/ol/webgl/PostProcessingPass.js index 5989ccb3bb..362dd1d1cf 100644 --- a/src/ol/webgl/PostProcessingPass.js +++ b/src/ol/webgl/PostProcessingPass.js @@ -2,6 +2,8 @@ * @module ol/webgl/PostProcessingPass */ +import {getUid} from '../util.js'; + const DEFAULT_VERTEX_SHADER = ` precision mediump float; @@ -265,7 +267,21 @@ class WebGLPostProcessingPass { gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, this.renderTargetTexture_); - // render the frame buffer to the canvas + if (!nextPass) { + // clear the canvas if we are the first to render to it + // and preserveDrawingBuffer is true + const canvasId = getUid(gl.canvas); + if (!frameState.renderTargets[canvasId]) { + const attributes = gl.getContextAttributes(); + if (attributes.preserveDrawingBuffer) { + gl.clearColor(0.0, 0.0, 0.0, 0.0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + frameState.renderTargets[canvasId] = true; + } + } + gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); diff --git a/test/browser/spec/ol/renderer/webgl/PointsLayer.test.js b/test/browser/spec/ol/renderer/webgl/PointsLayer.test.js index 171d412a2b..eddf3799da 100644 --- a/test/browser/spec/ol/renderer/webgl/PointsLayer.test.js +++ b/test/browser/spec/ol/renderer/webgl/PointsLayer.test.js @@ -25,6 +25,7 @@ const baseFrameState = { layerStatesArray: [{}], layerIndex: 0, pixelRatio: 1, + renderTargets: {}, }; const simpleVertexShader = ` @@ -664,6 +665,7 @@ describe('ol/renderer/webgl/PointsLayer', function () { zIndex: 0, }, ], + renderTargets: {}, }; }); diff --git a/test/browser/spec/ol/renderer/webgl/TileLayer.test.js b/test/browser/spec/ol/renderer/webgl/TileLayer.test.js index 65650a1c4b..2716ed4e22 100644 --- a/test/browser/spec/ol/renderer/webgl/TileLayer.test.js +++ b/test/browser/spec/ol/renderer/webgl/TileLayer.test.js @@ -51,6 +51,7 @@ describe('ol/renderer/webgl/TileLayer', function () { usedTiles: {}, wantedTiles: {}, tileQueue: new TileQueue(VOID, VOID), + renderTargets: {}, }; }); diff --git a/test/rendering/cases/webgl-opacity/expected.png b/test/rendering/cases/webgl-opacity/expected.png new file mode 100644 index 0000000000..e551c5c2e8 Binary files /dev/null and b/test/rendering/cases/webgl-opacity/expected.png differ diff --git a/test/rendering/cases/webgl-opacity/main.js b/test/rendering/cases/webgl-opacity/main.js new file mode 100644 index 0000000000..d8978b19e0 --- /dev/null +++ b/test/rendering/cases/webgl-opacity/main.js @@ -0,0 +1,45 @@ +import Heatmap from '../../../../src/ol/layer/Heatmap.js'; +import KML from '../../../../src/ol/format/KML.js'; +import Map from '../../../../src/ol/Map.js'; +import Tile from '../../../../src/ol/layer/Tile.js'; +import Vector from '../../../../src/ol/source/Vector.js'; +import View from '../../../../src/ol/View.js'; +import XYZ from '../../../../src/ol/source/XYZ.js'; +import {fromLonLat} from '../../../../src/ol/proj.js'; + +const map = new Map({ + layers: [ + new Tile({ + source: new XYZ({ + url: '/data/tiles/satellite/{z}/{x}/{y}.jpg', + transition: 0, + }), + }), + new Heatmap({ + source: new Vector({ + url: '/data/2012_Earthquakes_Mag5.kml', + format: new KML({ + extractStyles: false, + }), + }), + blur: 15, + radius: 5, + weight: function (feature) { + const name = feature.get('name'); + const magnitude = parseFloat(name.substr(2)); + return magnitude - 5; + }, + }), + ], + target: 'map', + view: new View({ + center: [0, 0], + zoom: 1, + }), +}); + +setTimeout(() => { + map.getView().setCenter(fromLonLat([45, 0])); + + render({message: 'Properly handles opacity in the first webgl layer'}); +}, 500);