Webgl points renderer / make the hit detection optional
This commit is contained in:
@@ -244,3 +244,7 @@ Layer opacity must be a number.
|
|||||||
### 65
|
### 65
|
||||||
|
|
||||||
A symbol literal representation must be defined on the style supplied to a `WebGLPointsLayer` instance.
|
A symbol literal representation must be defined on the style supplied to a `WebGLPointsLayer` instance.
|
||||||
|
|
||||||
|
### 66
|
||||||
|
|
||||||
|
`forEachFeatureAtCoordinate` cannot be used on a WebGL layer if the hit detection logic has not been enabled.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import {create as createWebGLWorker} from '../../worker/webgl.js';
|
import {create as createWebGLWorker} from '../../worker/webgl.js';
|
||||||
import {getUid} from '../../util.js';
|
import {getUid} from '../../util.js';
|
||||||
import WebGLRenderTarget from '../../webgl/RenderTarget.js';
|
import WebGLRenderTarget from '../../webgl/RenderTarget.js';
|
||||||
|
import {assert} from '../../asserts.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} CustomAttribute A description of a custom attribute to be passed on to the GPU, with a value different
|
* @typedef {Object} CustomAttribute A description of a custom attribute to be passed on to the GPU, with a value different
|
||||||
@@ -48,13 +49,38 @@ import WebGLRenderTarget from '../../webgl/RenderTarget.js';
|
|||||||
* All features will be rendered as quads (two triangles forming a square). New data will be flushed to the GPU
|
* All features will be rendered as quads (two triangles forming a square). New data will be flushed to the GPU
|
||||||
* every time the vector source changes.
|
* every time the vector source changes.
|
||||||
*
|
*
|
||||||
* Use shaders to customize the final output. The following attributes are available:
|
* You need to provide vertex and fragment shaders for rendering. This can be done using
|
||||||
* * `vec2 a_position`
|
* {@link module:ol/webgl/ShaderBuilder} utilities. These shaders shall expect a `a_position` attribute
|
||||||
* * `vec2 a_texCoord`
|
* containing the screen-space projected center of the quad, as well as a `a_index` attribute
|
||||||
* * `vec2 a_offsets`
|
* whose value (0, 1, 2 or 3) indicates which quad vertex is currently getting processed (see structure below).
|
||||||
* * `float a_rotateWithView`
|
*
|
||||||
* * `float a_opacity`
|
* To include variable attributes in the shaders, you need to declare them using the `attributes` property of
|
||||||
* * `float a_color`
|
* the options object like so:
|
||||||
|
* ```js
|
||||||
|
* new WebGLPointsLayerRenderer(layer, {
|
||||||
|
* attributes: [
|
||||||
|
* {
|
||||||
|
* name: 'size',
|
||||||
|
* callback: function(feature) {
|
||||||
|
* // compute something with the feature
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* name: 'weight',
|
||||||
|
* callback: function(feature) {
|
||||||
|
* // compute something with the feature
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* ],
|
||||||
|
* vertexShader:
|
||||||
|
* // shader using attribute a_weight and a_size
|
||||||
|
* fragmentShader:
|
||||||
|
* // shader using varying v_weight and v_size
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* To enable hit detection, you must as well provide dedicated shaders using the `hitVertexShader`
|
||||||
|
* and `hitFragmentShader` properties. These shall expect the `a_hitColor` attribute to contain
|
||||||
|
* the final color that will have to be output for hit detection to work.
|
||||||
*
|
*
|
||||||
* The following uniform is used for the main texture: `u_texture`.
|
* The following uniform is used for the main texture: `u_texture`.
|
||||||
*
|
*
|
||||||
@@ -77,61 +103,6 @@ import WebGLRenderTarget from '../../webgl/RenderTarget.js';
|
|||||||
*
|
*
|
||||||
* This uses {@link module:ol/webgl/Helper~WebGLHelper} internally.
|
* This uses {@link module:ol/webgl/Helper~WebGLHelper} internally.
|
||||||
*
|
*
|
||||||
* Default shaders are shown hereafter:
|
|
||||||
*
|
|
||||||
* * Vertex shader:
|
|
||||||
* ```
|
|
||||||
* precision mediump float;
|
|
||||||
*
|
|
||||||
* attribute vec2 a_position;
|
|
||||||
* attribute vec2 a_texCoord;
|
|
||||||
* attribute float a_rotateWithView;
|
|
||||||
* attribute vec2 a_offsets;
|
|
||||||
* attribute float a_opacity;
|
|
||||||
* attribute vec4 a_color;
|
|
||||||
*
|
|
||||||
* uniform mat4 u_projectionMatrix;
|
|
||||||
* uniform mat4 u_offsetScaleMatrix;
|
|
||||||
* uniform mat4 u_offsetRotateMatrix;
|
|
||||||
*
|
|
||||||
* varying vec2 v_texCoord;
|
|
||||||
* varying float v_opacity;
|
|
||||||
* varying vec4 v_color;
|
|
||||||
*
|
|
||||||
* void main(void) {
|
|
||||||
* mat4 offsetMatrix = u_offsetScaleMatrix;
|
|
||||||
* if (a_rotateWithView == 1.0) {
|
|
||||||
* offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;
|
|
||||||
* }
|
|
||||||
* vec4 offsets = offsetMatrix * vec4(a_offsets, 0.0, 0.0);
|
|
||||||
* gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;
|
|
||||||
* v_texCoord = a_texCoord;
|
|
||||||
* v_opacity = a_opacity;
|
|
||||||
* v_color = a_color;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* * Fragment shader:
|
|
||||||
* ```
|
|
||||||
* precision mediump float;
|
|
||||||
*
|
|
||||||
* uniform sampler2D u_texture;
|
|
||||||
*
|
|
||||||
* varying vec2 v_texCoord;
|
|
||||||
* varying float v_opacity;
|
|
||||||
* varying vec4 v_color;
|
|
||||||
*
|
|
||||||
* void main(void) {
|
|
||||||
* if (v_opacity == 0.0) {
|
|
||||||
* discard;
|
|
||||||
* }
|
|
||||||
* vec4 textureColor = texture2D(u_texture, v_texCoord);
|
|
||||||
* gl_FragColor = v_color * textureColor;
|
|
||||||
* gl_FragColor.a *= v_opacity;
|
|
||||||
* gl_FragColor.rgb *= gl_FragColor.a;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||||
@@ -161,7 +132,14 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
options.fragmentShader,
|
options.fragmentShader,
|
||||||
options.vertexShader
|
options.vertexShader
|
||||||
);
|
);
|
||||||
this.hitProgram_ = this.helper.getProgram(
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.hitDetectionEnabled_ = options.hitFragmentShader && options.hitVertexShader ? true : false;
|
||||||
|
|
||||||
|
this.hitProgram_ = this.hitDetectionEnabled_ && this.helper.getProgram(
|
||||||
options.hitFragmentShader,
|
options.hitFragmentShader,
|
||||||
options.hitVertexShader
|
options.hitVertexShader
|
||||||
);
|
);
|
||||||
@@ -255,7 +233,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
* @type {WebGLRenderTarget}
|
* @type {WebGLRenderTarget}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.hitRenderTarget_ = new WebGLRenderTarget(this.helper);
|
this.hitRenderTarget_ = this.hitDetectionEnabled_ && new WebGLRenderTarget(this.helper);
|
||||||
|
|
||||||
this.worker_ = createWebGLWorker();
|
this.worker_ = createWebGLWorker();
|
||||||
this.worker_.addEventListener('message', function(event) {
|
this.worker_.addEventListener('message', function(event) {
|
||||||
@@ -300,8 +278,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
canvas.style.opacity = opacity;
|
canvas.style.opacity = opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderHitDetection(frameState);
|
if (this.hitDetectionEnabled_) {
|
||||||
this.hitRenderTarget_.clearCachedData();
|
this.renderHitDetection(frameState);
|
||||||
|
this.hitRenderTarget_.clearCachedData();
|
||||||
|
}
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
@@ -370,9 +350,11 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
if (!this.renderInstructions_ || this.renderInstructions_.length !== totalInstructionsCount) {
|
if (!this.renderInstructions_ || this.renderInstructions_.length !== totalInstructionsCount) {
|
||||||
this.renderInstructions_ = new Float32Array(totalInstructionsCount);
|
this.renderInstructions_ = new Float32Array(totalInstructionsCount);
|
||||||
}
|
}
|
||||||
const totalHitInstructionsCount = (7 + this.customAttributes.length) * features.length;
|
if (this.hitDetectionEnabled_) {
|
||||||
if (!this.hitRenderInstructions_ || this.hitRenderInstructions_.length !== totalHitInstructionsCount) {
|
const totalHitInstructionsCount = (7 + this.customAttributes.length) * features.length;
|
||||||
this.hitRenderInstructions_ = new Float32Array(totalHitInstructionsCount);
|
if (!this.hitRenderInstructions_ || this.hitRenderInstructions_.length !== totalHitInstructionsCount) {
|
||||||
|
this.hitRenderInstructions_ = new Float32Array(totalHitInstructionsCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop on features to fill the buffer
|
// loop on features to fill the buffer
|
||||||
@@ -399,20 +381,24 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
|
|
||||||
// for hit detection, the feature uid is saved in the opacity value
|
// for hit detection, the feature uid is saved in the opacity value
|
||||||
// and the index of the opacity value is encoded in the color values
|
// and the index of the opacity value is encoded in the color values
|
||||||
this.hitRenderInstructions_[hitIndex++] = tmpCoords[0];
|
if (this.hitDetectionEnabled_) {
|
||||||
this.hitRenderInstructions_[hitIndex++] = tmpCoords[1];
|
this.hitRenderInstructions_[hitIndex++] = tmpCoords[0];
|
||||||
this.hitRenderInstructions_[hitIndex++] = hitColor[0];
|
this.hitRenderInstructions_[hitIndex++] = tmpCoords[1];
|
||||||
this.hitRenderInstructions_[hitIndex++] = hitColor[1];
|
this.hitRenderInstructions_[hitIndex++] = hitColor[0];
|
||||||
this.hitRenderInstructions_[hitIndex++] = hitColor[2];
|
this.hitRenderInstructions_[hitIndex++] = hitColor[1];
|
||||||
this.hitRenderInstructions_[hitIndex++] = hitColor[3];
|
this.hitRenderInstructions_[hitIndex++] = hitColor[2];
|
||||||
this.hitRenderInstructions_[hitIndex++] = Number(getUid(feature));
|
this.hitRenderInstructions_[hitIndex++] = hitColor[3];
|
||||||
|
this.hitRenderInstructions_[hitIndex++] = Number(getUid(feature));
|
||||||
|
}
|
||||||
|
|
||||||
// pushing custom attributes
|
// pushing custom attributes
|
||||||
let value;
|
let value;
|
||||||
for (let j = 0; j < this.customAttributes.length; j++) {
|
for (let j = 0; j < this.customAttributes.length; j++) {
|
||||||
value = this.customAttributes[j].callback(feature);
|
value = this.customAttributes[j].callback(feature);
|
||||||
this.renderInstructions_[renderIndex++] = value;
|
this.renderInstructions_[renderIndex++] = value;
|
||||||
this.hitRenderInstructions_[hitIndex++] = value;
|
if (this.hitDetectionEnabled_) {
|
||||||
|
this.hitRenderInstructions_[hitIndex++] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,21 +414,24 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
this.renderInstructions_ = null;
|
this.renderInstructions_ = null;
|
||||||
|
|
||||||
/** @type import('./Layer').WebGLWorkerGenerateBuffersMessage */
|
/** @type import('./Layer').WebGLWorkerGenerateBuffersMessage */
|
||||||
const hitMessage = {
|
if (this.hitDetectionEnabled_) {
|
||||||
type: WebGLWorkerMessageType.GENERATE_BUFFERS,
|
const hitMessage = {
|
||||||
renderInstructions: this.hitRenderInstructions_.buffer,
|
type: WebGLWorkerMessageType.GENERATE_BUFFERS,
|
||||||
customAttributesCount: 5 + this.customAttributes.length
|
renderInstructions: this.hitRenderInstructions_.buffer,
|
||||||
};
|
customAttributesCount: 5 + this.customAttributes.length
|
||||||
hitMessage['projectionTransform'] = projectionTransform;
|
};
|
||||||
hitMessage['hitDetection'] = true;
|
hitMessage['projectionTransform'] = projectionTransform;
|
||||||
this.worker_.postMessage(hitMessage, [this.hitRenderInstructions_.buffer]);
|
hitMessage['hitDetection'] = true;
|
||||||
this.hitRenderInstructions_ = null;
|
this.worker_.postMessage(hitMessage, [this.hitRenderInstructions_.buffer]);
|
||||||
|
this.hitRenderInstructions_ = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
|
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
|
||||||
|
assert(this.hitDetectionEnabled_, 66);
|
||||||
if (!this.hitRenderInstructions_) {
|
if (!this.hitRenderInstructions_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user