diff --git a/src/ol/layer/WebGLPoints.js b/src/ol/layer/WebGLPoints.js index 74fa63b039..7c9327e234 100644 --- a/src/ol/layer/WebGLPoints.js +++ b/src/ol/layer/WebGLPoints.js @@ -84,6 +84,8 @@ class WebGLPointsLayer extends Layer { return new WebGLPointsLayerRenderer(this, { vertexShader: this.parseResult_.builder.getSymbolVertexShader(), fragmentShader: this.parseResult_.builder.getSymbolFragmentShader(), + hitVertexShader: this.parseResult_.builder.getSymbolVertexShader(true), + hitFragmentShader: this.parseResult_.builder.getSymbolFragmentShader(true), uniforms: this.parseResult_.uniforms, attributes: this.parseResult_.attributes }); diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 71788a373c..45eb8e30b0 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -147,6 +147,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { options.hitVertexShader ); + if (this.getShaderCompileErrors()) { + throw new Error(this.getShaderCompileErrors()); + } + const customAttributes = options.attributes ? options.attributes.map(function(attribute) { return { diff --git a/src/ol/webgl/ShaderBuilder.js b/src/ol/webgl/ShaderBuilder.js index 666c76d3cd..adcd1b6d22 100644 --- a/src/ol/webgl/ShaderBuilder.js +++ b/src/ol/webgl/ShaderBuilder.js @@ -248,13 +248,27 @@ export class ShaderBuilder { * The following varyings are hardcoded and gives the coordinate of the pixel both in the quad and on the texture: * `vec2 v_quadCoord`, `vec2 v_texCoord` * + * @param {boolean} [forHitDetection] If true, the shader will be modified to include hit detection variables + * (namely, hit color with encoded feature id). * @returns {string} The full shader as a string. */ - getSymbolVertexShader() { + getSymbolVertexShader(forHitDetection) { const offsetMatrix = this.rotateWithView ? 'u_offsetScaleMatrix * u_offsetRotateMatrix' : 'u_offsetScaleMatrix'; + let attributes = this.attributes; + let varyings = this.varyings; + + if (forHitDetection) { + attributes = attributes.concat('vec4 a_hitColor'); + varyings = varyings.concat({ + name: 'v_hitColor', + type: 'vec4', + expression: 'a_hitColor' + }); + } + return `precision mediump float; uniform mat4 u_projectionMatrix; uniform mat4 u_offsetScaleMatrix; @@ -265,12 +279,12 @@ ${this.uniforms.map(function(uniform) { }).join('\n')} attribute vec2 a_position; attribute float a_index; -${this.attributes.map(function(attribute) { +${attributes.map(function(attribute) { return 'attribute ' + attribute + ';'; }).join('\n')} varying vec2 v_texCoord; varying vec2 v_quadCoord; -${this.varyings.map(function(varying) { +${varyings.map(function(varying) { return 'varying ' + varying.type + ' ' + varying.name + ';'; }).join('\n')} void main(void) { @@ -288,7 +302,7 @@ void main(void) { u = a_index == 0.0 || a_index == 3.0 ? 0.0 : 1.0; v = a_index == 2.0 || a_index == 3.0 ? 0.0 : 1.0; v_quadCoord = vec2(u, v); -${this.varyings.map(function(varying) { +${varyings.map(function(varying) { return ' ' + varying.name + ' = ' + varying.expression + ';'; }).join('\n')} }`; @@ -301,9 +315,24 @@ ${this.varyings.map(function(varying) { * Expects the following varyings to be transmitted by the vertex shader: * `vec2 v_quadCoord`, `vec2 v_texCoord` * + * @param {boolean} [forHitDetection] If true, the shader will be modified to include hit detection variables + * (namely, hit color with encoded feature id). * @returns {string} The full shader as a string. */ - getSymbolFragmentShader() { + getSymbolFragmentShader(forHitDetection) { + const hitDetectionBypass = forHitDetection ? + ' if (gl_FragColor.a < 0.1) { discard; } gl_FragColor = v_hitColor;' : ''; + + let varyings = this.varyings; + + if (forHitDetection) { + varyings = varyings.concat({ + name: 'v_hitColor', + type: 'vec4', + expression: 'a_hitColor' + }); + } + return `precision mediump float; uniform float u_time; ${this.uniforms.map(function(uniform) { @@ -311,13 +340,14 @@ ${this.uniforms.map(function(uniform) { }).join('\n')} varying vec2 v_texCoord; varying vec2 v_quadCoord; -${this.varyings.map(function(varying) { +${varyings.map(function(varying) { return 'varying ' + varying.type + ' ' + varying.name + ';'; }).join('\n')} void main(void) { if (${this.discardExpression}) { discard; } gl_FragColor = ${this.colorExpression}; gl_FragColor.rgb *= gl_FragColor.a; +${hitDetectionBypass} }`; } } diff --git a/test/spec/ol/webgl/shaderbuilder.test.js b/test/spec/ol/webgl/shaderbuilder.test.js index f6a7eafb25..92a459688c 100644 --- a/test/spec/ol/webgl/shaderbuilder.test.js +++ b/test/spec/ol/webgl/shaderbuilder.test.js @@ -120,6 +120,39 @@ void main(void) { v = a_index == 2.0 || a_index == 3.0 ? 0.0 : 1.0; v_quadCoord = vec2(u, v); +}`); + }); + it('generates a symbol vertex shader for hitDetection', function() { + const builder = new ShaderBuilder(); + + expect(builder.getSymbolVertexShader(true)).to.eql(`precision mediump float; +uniform mat4 u_projectionMatrix; +uniform mat4 u_offsetScaleMatrix; +uniform mat4 u_offsetRotateMatrix; +uniform float u_time; + +attribute vec2 a_position; +attribute float a_index; +attribute vec4 a_hitColor; +varying vec2 v_texCoord; +varying vec2 v_quadCoord; +varying vec4 v_hitColor; +void main(void) { + mat4 offsetMatrix = u_offsetScaleMatrix; + vec2 size = vec2(1.0); + vec2 offset = vec2(0.0); + float offsetX = a_index == 0.0 || a_index == 3.0 ? offset.x - size.x / 2.0 : offset.x + size.x / 2.0; + float offsetY = a_index == 0.0 || a_index == 1.0 ? offset.y - size.y / 2.0 : offset.y + size.y / 2.0; + vec4 offsets = offsetMatrix * vec4(offsetX, offsetY, 0.0, 0.0); + gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets; + vec4 texCoord = vec4(0.0, 0.0, 1.0, 1.0); + float u = a_index == 0.0 || a_index == 3.0 ? texCoord.s : texCoord.p; + float v = a_index == 2.0 || a_index == 3.0 ? texCoord.t : texCoord.q; + v_texCoord = vec2(u, v); + u = a_index == 0.0 || a_index == 3.0 ? 0.0 : 1.0; + v = a_index == 2.0 || a_index == 3.0 ? 0.0 : 1.0; + v_quadCoord = vec2(u, v); + v_hitColor = a_hitColor; }`); }); }); @@ -145,6 +178,7 @@ void main(void) { if (false) { discard; } gl_FragColor = vec4(0.3137254901960784, 0.0, 1.0, 1.0); gl_FragColor.rgb *= gl_FragColor.a; + }`); }); it('generates a symbol fragment shader (with uniforms)', function() { @@ -168,6 +202,23 @@ void main(void) { if (u_myUniform > 0.5) { discard; } gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); gl_FragColor.rgb *= gl_FragColor.a; + +}`); + }); + it('generates a symbol fragment shader for hit detection', function() { + const builder = new ShaderBuilder(); + + expect(builder.getSymbolFragmentShader(true)).to.eql(`precision mediump float; +uniform float u_time; + +varying vec2 v_texCoord; +varying vec2 v_quadCoord; +varying vec4 v_hitColor; +void main(void) { + if (false) { discard; } + gl_FragColor = vec4(1.0); + gl_FragColor.rgb *= gl_FragColor.a; + if (gl_FragColor.a < 0.1) { discard; } gl_FragColor = v_hitColor; }`); }); });