Add a new WebGLPointsLayer type using the shader builder utilities

This required adding a `a_index` attribute in the points layer renderer
to be able to make the precomputed shaders to work.
This commit is contained in:
Olivier Guyot
2019-09-24 17:24:54 +02:00
parent a6b8d920b7
commit 4d7562fca2
6 changed files with 142 additions and 19 deletions

View File

@@ -240,3 +240,7 @@ Support for the `OES_element_index_uint` WebGL extension is mandatory for WebGL
### 64
Layer opacity must be a number.
### 65
A symbol literal representation must be defined on the style supplied to a `WebGLPointsLayer` instance.

View File

@@ -0,0 +1,69 @@
/**
* @module ol/layer/WebGLPoints
*/
import VectorLayer from './Vector.js';
import {assign} from '../obj.js';
import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer.js';
import {getSymbolFragmentShader, getSymbolVertexShader} from '../webgl/ShaderBuilder.js';
import {assert} from '../asserts.js';
/**
* @typedef {Object} Options
* @property {import('../style/LiteralStyle.js').LiteralStyle} literalStyle Literal style to apply to the layer features.
* @property {string} [className='ol-layer'] A CSS class name to set to the layer element.
* @property {number} [opacity=1] Opacity (0, 1).
* @property {boolean} [visible=true] Visibility.
* @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be
* rendered outside of this extent.
* @property {number} [zIndex] The z-index for layer rendering. At rendering time, the layers
* will be ordered, first by Z-index and then by position. When `undefined`, a `zIndex` of 0 is assumed
* for layers that are added to the map's `layers` collection, or `Infinity` when the layer's `setMap()`
* method was used.
* @property {number} [minResolution] The minimum resolution (inclusive) at which this layer will be
* visible.
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible.
* @property {import("../source/Vector.js").default} [source] Source.
*/
/**
* @classdesc
* Layer optimized for rendering large point datasets.
* Note that any property set in the options is set as a {@link module:ol/Object~BaseObject}
* property on the layer object; for example, setting `title: 'My Title'` in the
* options means that `title` is observable, and has get/set accessors.
*
* @fires import("../render/Event.js").RenderEvent
* @api
*/
class WebGLPointsLayer extends VectorLayer {
/**
* @param {Options} options Options.
*/
constructor(options) {
const baseOptions = assign({}, options);
super(baseOptions);
/**
* @type {import('../style/LiteralStyle.js').LiteralStyle}
*/
this.literalStyle = options.literalStyle;
assert(this.literalStyle.symbol !== undefined, 65);
}
/**
* @inheritDoc
*/
createRenderer() {
return new WebGLPointsLayerRenderer(this, {
vertexShader: getSymbolVertexShader(this.literalStyle.symbol),
fragmentShader: getSymbolFragmentShader()
});
}
}
export default WebGLPointsLayer;

View File

@@ -123,9 +123,9 @@ const tmpArray_ = [];
const bufferPositions_ = {vertexPosition: 0, indexPosition: 0};
export const POINT_INSTRUCTIONS_COUNT = 13;
export const POINT_VERTEX_STRIDE = 12;
export const POINT_VERTEX_STRIDE = 13;
function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha) {
function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, rotateWithView, red, green, blue, alpha, index) {
buffer[pos + 0] = x;
buffer[pos + 1] = y;
buffer[pos + 2] = offsetX;
@@ -138,6 +138,7 @@ function writePointVertex(buffer, pos, x, y, offsetX, offsetY, u, v, opacity, ro
buffer[pos + 9] = green;
buffer[pos + 10] = blue;
buffer[pos + 11] = alpha;
buffer[pos + 12] = index;
}
function writeCustomAttrs(buffer, pos, customAttrs) {
@@ -202,19 +203,19 @@ export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuf
const baseIndex = vPos / stride;
// push vertices for each of the four quad corners (first standard then custom attributes)
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha);
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha, 0);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha);
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha, 1);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha);
writePointVertex(vertexBuffer, vPos, x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha, 2);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha);
writePointVertex(vertexBuffer, vPos, x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha, 3);
writeCustomAttrs(vertexBuffer, vPos + baseStride, customAttrs);
vPos += stride;

View File

@@ -408,6 +408,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.helper.enableAttributeArray(DefaultAttrib.OPACITY, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 6);
this.helper.enableAttributeArray(DefaultAttrib.ROTATE_WITH_VIEW, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 7);
this.helper.enableAttributeArray(DefaultAttrib.COLOR, 4, FLOAT, bytesPerFloat * stride, bytesPerFloat * 8);
this.helper.enableAttributeArray('a_index', 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 12);
return true;
}
@@ -568,6 +569,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.helper.enableAttributeArray(DefaultAttrib.OPACITY, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 6);
this.helper.enableAttributeArray(DefaultAttrib.ROTATE_WITH_VIEW, 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 7);
this.helper.enableAttributeArray(DefaultAttrib.COLOR, 4, FLOAT, bytesPerFloat * stride, bytesPerFloat * 8);
this.helper.enableAttributeArray('a_index', 1, FLOAT, bytesPerFloat * stride, bytesPerFloat * 12);
const renderCount = this.indicesBuffer_.getSize();
this.helper.drawElements(0, renderCount);

View File

@@ -0,0 +1,56 @@
/**
* Literal Style objects differ from standard styles in that they cannot
* be functions and are made up of simple objects instead of classes.
* @module ol/style/LiteralStyle
*/
/**
* Here are a few samples of literal style objects:
* ```js
* const style = {
* symbol: {
* symbolType: 'circle',
* size: 8,
* color: '#33AAFF',
* opacity: 0.9
* }
* }
* ```
*
* ```js
* const style = {
* symbol: {
* symbolType: 'image',
* offset: [0, 12],
* size: [4, 8],
* src: '../static/exclamation-mark.png'
* }
* }
* ```
*
* @typedef {Object} LiteralStyle
* @property {LiteralSymbolStyle} [symbol] Symbol representation.
*/
/**
* @enum {string}
*/
export const SymbolType = {
CIRCLE: 'circle',
SQUARE: 'square',
TRIANGLE: 'triangle',
IMAGE: 'image'
};
/**
* @typedef {Object} LiteralSymbolStyle
* @property {number|Array.<number, number>} size Size, mandatory.
* @property {SymbolType} symbolType Symbol type to use, either a regular shape or an image.
* @property {string} [src] Path to the image to be used for the symbol. Only required with `symbolType: 'image'`.
* @property {import("../color.js").Color|string} [color='#FFFFFF'] Color used for the representation (either fill, line or symbol).
* @property {number} [opacity=0] Opacity.
* @property {Array.<number, number>} [offset] Offset on X and Y axis for symbols. If not specified, the symbol will be centered.
* @property {Array.<number, number, number, number>} [textureCoord] Texture coordinates. If not specified, the whole texture will be used (range for 0 to 1 on both axes).
* @property {boolean} [rotateWithView=false] Specify whether the symbol must rotate with the view or stay upwards.
*/

View File

@@ -16,17 +16,8 @@ export function formatNumber(v) {
}
/**
* @typedef {Object} SymbolShaderParameters
* @property {number|Array.<number, number>} size Size.
* @property {boolean} [rotateWithView] Rotate with view.
* @property {Array.<number, number>} [offset] Offset.
* @property {Array.<number, number, number, number>} [textureCoord] Texture coordinates: u0, v0, u1, v1.
* @property {number} [opacity] Opacity.
* @property {import("../color.js").Color|string} [color] Color.
*/
/**
* Generates a symbol vertex shader, i.e. a shader intended to be used on point geometries.
* Generates a symbol vertex shader from a literal style,
* intended to be used on point geometries.
*
* Expected the following attributes to be present in the attribute array:
* `vec2 a_position`, `float a_index` (being the index of the vertex in the quad, 0 to 3).
@@ -34,7 +25,7 @@ export function formatNumber(v) {
* Transmits the following varyings to the fragment shader:
* `vec2 v_texCoord`, `float v_opacity`, `vec4 v_color`
*
* @param {SymbolShaderParameters} parameters Parameters for the shader.
* @param {import('../style/LiteralStyle.js').LiteralSymbolStyle} parameters Parameters for the shader.
* @returns {string} The full shader as a string.
*/
export function getSymbolVertexShader(parameters) {
@@ -82,7 +73,7 @@ void main(void) {
}
/**
* Generates a symbol fragment shader, i.e. a shader intended to be used on point geometries.
* Generates a symbol fragment shader intended to be used on point geometries.
*
* Expected the following varyings to be transmitted by the vertex shader:
* `vec2 v_texCoord`, `float v_opacity`, `vec4 v_color`