diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 2d41b7cead..b259041c07 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -540,7 +540,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.previousExtent_ = frameState.extent.slice(); } - this.helper.useProgram(this.program_); + this.helper.useProgram(this.program_, frameState); this.helper.prepareDraw(frameState); // write new data @@ -726,7 +726,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { Math.floor(frameState.size[1] / 2), ]); - this.helper.useProgram(this.hitProgram_); + this.helper.useProgram(this.hitProgram_, frameState); this.helper.prepareDrawToRenderTarget( frameState, this.hitRenderTarget_, diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 7dbf1cb13e..836d6580de 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -508,7 +508,7 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { } } - this.helper.useProgram(this.program_); + this.helper.useProgram(this.program_, frameState); this.helper.prepareDraw(frameState, !blend); const zs = Object.keys(tileTexturesByZ) diff --git a/src/ol/webgl/Helper.js b/src/ol/webgl/Helper.js index 383b06e848..ba38afaf53 100644 --- a/src/ol/webgl/Helper.js +++ b/src/ol/webgl/Helper.js @@ -49,6 +49,7 @@ export const DefaultUniform = { TIME: 'u_time', ZOOM: 'u_zoom', RESOLUTION: 'u_resolution', + SIZE_PX: 'u_sizePx', }; /** @@ -206,11 +207,12 @@ function releaseCanvas(key) { * Shaders must be compiled and assembled into a program like so: * ```js * // here we simply create two shaders and assemble them in a program which is then used - * // for subsequent rendering calls + * // for subsequent rendering calls; note how a frameState is required to set up a program, + * // as several default uniforms are computed from it (projection matrix, zoom level, etc.) * const vertexShader = new WebGLVertex(VERTEX_SHADER); * const fragmentShader = new WebGLFragment(FRAGMENT_SHADER); * const program = this.context.getProgram(fragmentShader, vertexShader); - * helper.useProgram(this.program); + * helper.useProgram(this.program, frameState); * ``` * * Uniforms are defined using the `uniforms` option and can either be explicit values or callbacks taking the frame state as argument. @@ -564,8 +566,6 @@ class WebGLHelper extends Disposable { canvas.style.width = size[0] + 'px'; canvas.style.height = size[1] + 'px'; - gl.useProgram(this.currentProgram_); - // loop backwards in post processes list for (let i = this.postProcessPasses_.length - 1; i >= 0; i--) { this.postProcessPasses_[i].init(frameState); @@ -581,10 +581,6 @@ class WebGLHelper extends Disposable { gl.ONE, opt_disableAlphaBlend ? gl.ZERO : gl.ONE_MINUS_SRC_ALPHA ); - - gl.useProgram(this.currentProgram_); - this.applyFrameState(frameState); - this.applyUniforms(frameState); } /** @@ -609,10 +605,6 @@ class WebGLHelper extends Disposable { gl.ONE, opt_disableAlphaBlend ? gl.ZERO : gl.ONE_MINUS_SRC_ALPHA ); - - gl.useProgram(this.currentProgram_); - this.applyFrameState(frameState); - this.applyUniforms(frameState); } /** @@ -709,6 +701,7 @@ class WebGLHelper extends Disposable { DefaultUniform.RESOLUTION, frameState.viewState.resolution ); + this.setUniformFloatVec2(DefaultUniform.SIZE_PX, [size[0], size[1]]); } /** @@ -803,22 +796,20 @@ class WebGLHelper extends Disposable { } /** - * Use a program. If the program is already in use, this will return `false`. + * Set up a program for use. The program will be set as the current one. Then, the uniforms used + * in the program will be set based on the current frame state and the helper configuration. * @param {WebGLProgram} program Program. - * @return {boolean} Changed. + * @param {import("../PluggableMap.js").FrameState} frameState Frame state. * @api */ - useProgram(program) { - if (program == this.currentProgram_) { - return false; - } else { - const gl = this.getGL(); - gl.useProgram(program); - this.currentProgram_ = program; - this.uniformLocations_ = {}; - this.attribLocations_ = {}; - return true; - } + useProgram(program, frameState) { + const gl = this.getGL(); + gl.useProgram(program); + this.currentProgram_ = program; + this.uniformLocations_ = {}; + this.attribLocations_ = {}; + this.applyFrameState(frameState); + this.applyUniforms(frameState); } /** @@ -959,6 +950,15 @@ class WebGLHelper extends Disposable { this.getGL().uniform1f(this.getUniformLocation(uniform), value); } + /** + * Give a value for a vec2 uniform + * @param {string} uniform Uniform name + * @param {Array} value Array of length 4. + */ + setUniformFloatVec2(uniform, value) { + this.getGL().uniform2fv(this.getUniformLocation(uniform), value); + } + /** * Give a value for a vec4 uniform * @param {string} uniform Uniform name diff --git a/test/browser/spec/ol/webgl/helper.test.js b/test/browser/spec/ol/webgl/helper.test.js index 1b4a194eba..67fcef2fc4 100644 --- a/test/browser/spec/ol/webgl/helper.test.js +++ b/test/browser/spec/ol/webgl/helper.test.js @@ -57,6 +57,15 @@ const INVALID_FRAGMENT_SHADER = ` gl_FragColor = vec4(oops, 1.0, 1.0, 1.0); }`; +const SAMPLE_FRAMESTATE = { + size: [100, 150], + viewState: { + rotation: 0.4, + resolution: 2, + center: [10, 20], + }, +}; + describe('ol/webgl/WebGLHelper', function () { let h; afterEach(function () { @@ -117,7 +126,10 @@ describe('ol/webgl/WebGLHelper', function () { u_test4: createTransform(), }, }); - h.useProgram(h.getProgram(FRAGMENT_SHADER, VERTEX_SHADER)); + h.useProgram( + h.getProgram(FRAGMENT_SHADER, VERTEX_SHADER), + SAMPLE_FRAMESTATE + ); h.prepareDraw({ pixelRatio: 2, size: [50, 80], @@ -164,7 +176,7 @@ describe('ol/webgl/WebGLHelper', function () { h = new WebGLHelper(); p = h.getProgram(FRAGMENT_SHADER, VERTEX_SHADER); - h.useProgram(p); + h.useProgram(p, SAMPLE_FRAMESTATE); }); it('has saved the program', function () { @@ -209,34 +221,30 @@ describe('ol/webgl/WebGLHelper', function () { }); describe('#makeProjectionTransform', function () { - let frameState; beforeEach(function () { h = new WebGLHelper(); - - frameState = { - size: [100, 150], - viewState: { - rotation: 0.4, - resolution: 2, - center: [10, 20], - }, - }; }); it('gives out the correct transform', function () { - const scaleX = 2 / frameState.size[0] / frameState.viewState.resolution; - const scaleY = 2 / frameState.size[1] / frameState.viewState.resolution; + const scaleX = + 2 / + SAMPLE_FRAMESTATE.size[0] / + SAMPLE_FRAMESTATE.viewState.resolution; + const scaleY = + 2 / + SAMPLE_FRAMESTATE.size[1] / + SAMPLE_FRAMESTATE.viewState.resolution; const given = createTransform(); const expected = createTransform(); scaleTransform(expected, scaleX, scaleY); - rotateTransform(expected, -frameState.viewState.rotation); + rotateTransform(expected, -SAMPLE_FRAMESTATE.viewState.rotation); translateTransform( expected, - -frameState.viewState.center[0], - -frameState.viewState.center[1] + -SAMPLE_FRAMESTATE.viewState.center[0], + -SAMPLE_FRAMESTATE.viewState.center[1] ); - h.makeProjectionTransform(frameState, given); + h.makeProjectionTransform(SAMPLE_FRAMESTATE, given); expect(given.map((val) => val.toFixed(15))).to.eql( expected.map((val) => val.toFixed(15)) @@ -377,7 +385,8 @@ describe('ol/webgl/WebGLHelper', function () { void main(void) { gl_Position = vec4(u_test, attr3, 0.0, 1.0); }` - ) + ), + SAMPLE_FRAMESTATE ); });