Added documentation & fixed linting for WebGL classes
This commit is contained in:
@@ -10,6 +10,28 @@ Breaking change: layers can no longer be shared between several `Map` objects.
|
||||
Breaking change: the `Graticule` control has been replaced by a layer also called `Graticule`, found in `ol/layer/Graticule`.
|
||||
The API remains similar.
|
||||
|
||||
#### Breaking change: drop of support for most of WebGL features
|
||||
|
||||
The WebGL map and layers renderers are gone, replaced by a `WebGLHelper` function that provides a lightweight,
|
||||
low-level access to the WebGL API. This is implemented in a new `WebGLPointsLayer` which does simple rendering of large number
|
||||
of points with custom shaders.
|
||||
|
||||
This is now used in the `Heatmap` layer.
|
||||
|
||||
The removed classes and components are:
|
||||
* `WebGLMap` and `WebGLMapRenderer`
|
||||
* `WebGLLayerRenderer`
|
||||
* `WebGLImageLayer` and `WebGLImageLayerRenderer`
|
||||
* `WebGLTileLayer` and `WebGLTileLayerRenderer`
|
||||
* `WebGLVectorLayer` and `WebGLVectorLayerRenderer`
|
||||
* `WebGLReplay` and derived classes, along with associated shaders
|
||||
* `WebGLReplayGroup`
|
||||
* `WebGLImmediateRenderer`
|
||||
* `WebGLMap`
|
||||
* The shader build process using `mustache` and the `Makefile` at the root
|
||||
|
||||
|
||||
|
||||
### v5.3.0
|
||||
|
||||
#### The `getUid` function returns string
|
||||
|
||||
@@ -75,7 +75,6 @@
|
||||
"loglevelnext": "^3.0.0",
|
||||
"marked": "0.5.1",
|
||||
"mocha": "5.2.0",
|
||||
"mustache": "^3.0.0",
|
||||
"ol-mapbox-style": "^3.3.0",
|
||||
"pixelmatch": "^4.0.2",
|
||||
"pngjs": "^3.3.3",
|
||||
|
||||
@@ -6,7 +6,7 @@ import {getChangeEventType} from '../Object.js';
|
||||
import {createCanvasContext2D} from '../dom.js';
|
||||
import VectorLayer from './Vector.js';
|
||||
import {assign} from '../obj.js';
|
||||
import WebGLPointsLayerRenderer from "../renderer/webgl-new/PointsLayer";
|
||||
import WebGLPointsLayerRenderer from '../renderer/webgl-new/PointsLayer';
|
||||
|
||||
|
||||
/**
|
||||
@@ -315,11 +315,11 @@ class Heatmap extends VectorLayer {
|
||||
}`,
|
||||
scaleRatio: 0.5,
|
||||
uniforms: {
|
||||
u_blurSize: function (frameState) {
|
||||
u_blurSize: function(frameState) {
|
||||
return [
|
||||
this.get(Property.BLUR) / frameState.size[0],
|
||||
this.get(Property.BLUR) / frameState.size[1]
|
||||
]
|
||||
];
|
||||
}.bind(this)
|
||||
}
|
||||
},
|
||||
@@ -339,13 +339,13 @@ class Heatmap extends VectorLayer {
|
||||
gl_FragColor.a = color.a;
|
||||
}`,
|
||||
uniforms: {
|
||||
u_gradientTexture: this.gradient_,
|
||||
u_gradientTexture: this.gradient_
|
||||
}
|
||||
}
|
||||
],
|
||||
sizeCallback: function(feature) {
|
||||
return this.weightFunction_(feature);
|
||||
}.bind(this),
|
||||
}.bind(this)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class RenderEvent extends Event {
|
||||
* @param {import("./VectorContext.js").default=} opt_vectorContext Vector context.
|
||||
* @param {import("../PluggableMap.js").FrameState=} opt_frameState Frame state.
|
||||
* @param {?CanvasRenderingContext2D=} opt_context Context.
|
||||
* @param {?import("../webgl/Context.js").default=} opt_glContext WebGL Context.
|
||||
* @param {?import("../webgl/Helper.js").default=} opt_glContext WebGL Context.
|
||||
*/
|
||||
constructor(type, opt_vectorContext, opt_frameState, opt_context, opt_glContext) {
|
||||
|
||||
@@ -42,7 +42,7 @@ class RenderEvent extends Event {
|
||||
/**
|
||||
* WebGL context. Only available when a WebGL renderer is used, null
|
||||
* otherwise.
|
||||
* @type {import("../webgl/Context.js").default|null|undefined}
|
||||
* @type {import("../webgl/Helper.js").default|null|undefined}
|
||||
* @api
|
||||
*/
|
||||
this.glContext = opt_glContext;
|
||||
|
||||
@@ -5,9 +5,9 @@ import LayerRenderer from '../Layer';
|
||||
import WebGLBuffer from '../../webgl/Buffer';
|
||||
import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl';
|
||||
import WebGLHelper, {DefaultAttrib, DefaultUniform} from '../../webgl/Helper';
|
||||
import WebGLVertex from "../../webgl/Vertex";
|
||||
import WebGLFragment from "../../webgl/Fragment";
|
||||
import GeometryType from "../../geom/GeometryType";
|
||||
import WebGLVertex from '../../webgl/Vertex';
|
||||
import WebGLFragment from '../../webgl/Fragment';
|
||||
import GeometryType from '../../geom/GeometryType';
|
||||
|
||||
const VERTEX_SHADER = `
|
||||
precision mediump float;
|
||||
@@ -47,16 +47,89 @@ const FRAGMENT_SHADER = `
|
||||
gl_FragColor.a = alpha;
|
||||
}`;
|
||||
|
||||
/**
|
||||
* @typedef {Object} PostProcessesOptions
|
||||
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
|
||||
* the main canvas that will then be sampled up (useful for saving resource on blur steps).
|
||||
* @property {string} [vertexShader] Vertex shader source
|
||||
* @property {string} [fragmentShader] Fragment shader source
|
||||
* @property {Object.<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process step
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {function(import("../../Feature").default):number} [sizeCallback] Will be called on every feature in the
|
||||
* source to compute the size of the quad on screen (in pixels). This only done on source change.
|
||||
* @property {function(import("../../Feature").default, number):number} [coordCallback] Will be called on every feature in the
|
||||
* source to compute the coordinate of the quad center on screen (in pixels). This only done on source change.
|
||||
* The second argument is 0 for `x` component and 1 for `y`.
|
||||
* @property {string} [vertexShader] Vertex shader source
|
||||
* @property {string} [fragmentShader] Fragment shader source
|
||||
* @property {Object.<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process steps
|
||||
* @property {Array<PostProcessesOptions>} [postProcesses] Post-processes definitions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Webgl vector renderer optimized for points.
|
||||
* All features will be rendered as points.
|
||||
* WebGL vector renderer optimized for points.
|
||||
* 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.
|
||||
*
|
||||
* Use shaders to customize the final output.
|
||||
*
|
||||
* 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;
|
||||
*
|
||||
* uniform mat4 u_projectionMatrix;
|
||||
* uniform mat4 u_offsetScaleMatrix;
|
||||
* uniform mat4 u_offsetRotateMatrix;
|
||||
*
|
||||
* varying vec2 v_texCoord;
|
||||
*
|
||||
* 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;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* * Fragment shader:
|
||||
* ```
|
||||
* precision mediump float;
|
||||
* uniform float u_opacity;
|
||||
*
|
||||
* varying vec2 v_texCoord;
|
||||
*
|
||||
* void main(void) {
|
||||
* gl_FragColor.rgb = vec3(1.0, 1.0, 1.0);
|
||||
* float alpha = u_opacity;
|
||||
* if (alpha == 0.0) {
|
||||
* discard;
|
||||
* }
|
||||
* gl_FragColor.a = alpha;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
|
||||
/**
|
||||
* @param {import("../../layer/Vector.js").default} vectorLayer Vector layer.
|
||||
* @param {Options=} [opt_options] Options.
|
||||
*/
|
||||
constructor(vectorLayer, opt_options) {
|
||||
super(vectorLayer);
|
||||
@@ -84,7 +157,7 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
this.coordCallback_ = options.coordCallback || function(feature, index) {
|
||||
const geom = /** @type {import("../../geom/Point").default} */ (feature.getGeometry());
|
||||
return geom.getCoordinates()[index];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,18 +199,17 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
|
||||
return;
|
||||
}
|
||||
const geom = /** @type {import("../../geom/Point").default} */ (feature.getGeometry());
|
||||
const x = this.coordCallback_(feature, 0);
|
||||
const y = this.coordCallback_(feature, 1);
|
||||
const size = this.sizeCallback_(feature);
|
||||
let stride = 6;
|
||||
let baseIndex = this.verticesBuffer_.getArray().length / stride;
|
||||
const stride = 6;
|
||||
const baseIndex = this.verticesBuffer_.getArray().length / stride;
|
||||
|
||||
this.verticesBuffer_.getArray().push(
|
||||
x, y, -size / 2, -size / 2, 0, 0,
|
||||
x, y, +size / 2, -size / 2, 1, 0,
|
||||
x, y, +size / 2, +size / 2, 1, 1,
|
||||
x, y, -size / 2, +size / 2, 0, 1,
|
||||
x, y, -size / 2, +size / 2, 0, 1
|
||||
);
|
||||
this.indicesBuffer_.getArray().push(
|
||||
baseIndex, baseIndex + 1, baseIndex + 3,
|
||||
@@ -150,7 +222,7 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
this.context_.bindBuffer(ARRAY_BUFFER, this.verticesBuffer_);
|
||||
this.context_.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||
|
||||
let bytesPerFloat = Float32Array.BYTES_PER_ELEMENT;
|
||||
const bytesPerFloat = Float32Array.BYTES_PER_ELEMENT;
|
||||
this.context_.enableAttributeArray(DefaultAttrib.POSITION, 2, FLOAT, bytesPerFloat * 6, 0);
|
||||
this.context_.enableAttributeArray(DefaultAttrib.OFFSETS, 2, FLOAT, bytesPerFloat * 6, bytesPerFloat * 2);
|
||||
this.context_.enableAttributeArray(DefaultAttrib.TEX_COORD, 2, FLOAT, bytesPerFloat * 6, bytesPerFloat * 4);
|
||||
|
||||
@@ -15,10 +15,10 @@ import {
|
||||
rotate as rotateTransform,
|
||||
scale as scaleTransform,
|
||||
translate as translateTransform
|
||||
} from "../transform";
|
||||
import {create, fromTransform} from "../vec/mat4";
|
||||
import WebGLBuffer from "./Buffer";
|
||||
import WebGLPostProcessingPass from "./PostProcessingPass";
|
||||
} from '../transform';
|
||||
import {create, fromTransform} from '../vec/mat4';
|
||||
import WebGLBuffer from './Buffer';
|
||||
import WebGLPostProcessingPass from './PostProcessingPass';
|
||||
|
||||
|
||||
/**
|
||||
@@ -27,6 +27,11 @@ import WebGLPostProcessingPass from "./PostProcessingPass";
|
||||
* @property {WebGLBuffer} buffer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Uniform names used in the default shaders.
|
||||
* @const
|
||||
* @type {Object.<string,string>}
|
||||
*/
|
||||
export const DefaultUniform = {
|
||||
PROJECTION_MATRIX: 'u_projectionMatrix',
|
||||
OFFSET_SCALE_MATRIX: 'u_offsetScaleMatrix',
|
||||
@@ -34,6 +39,11 @@ export const DefaultUniform = {
|
||||
OPACITY: 'u_opacity'
|
||||
};
|
||||
|
||||
/**
|
||||
* Attribute names used in the default shaders.
|
||||
* @const
|
||||
* @type {Object.<string,string>}
|
||||
*/
|
||||
export const DefaultAttrib = {
|
||||
POSITION: 'a_position',
|
||||
TEX_COORD: 'a_texCoord',
|
||||
@@ -42,15 +52,149 @@ export const DefaultAttrib = {
|
||||
OFFSETS: 'a_offsets'
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {number|Array<number>|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} UniformLiteralValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* Uniform value can be a number, array of numbers (2 to 4), canvas element or a callback returning
|
||||
* one of the previous types.
|
||||
* @typedef {UniformLiteralValue|function(import("../PluggableMap.js").FrameState):UniformLiteralValue} UniformValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PostProcessesOptions
|
||||
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
|
||||
* the main canvas that will then be sampled up (useful for saving resource on blur steps).
|
||||
* @property {string} [vertexShader] Vertex shader source
|
||||
* @property {string} [fragmentShader] Fragment shader source
|
||||
* @property {Object.<string,UniformValue>} [uniforms] Uniform definitions for the post process step
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {Object.<string,UniformValue>} [uniforms] Uniform definitions; property namesmust math the uniform
|
||||
* names in the provided or default shaders.
|
||||
* @property {Array<PostProcessesOptions>} [postProcesses] Post-processes definitions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} UniformInternalDescription
|
||||
* @property {string} name Name
|
||||
* @property {WebGLTexture} [texture] Texture
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* A WebGL context for accessing low-level WebGL capabilities.
|
||||
* Will handle attributes, uniforms, buffers, textures, frame buffers.
|
||||
* The context will always render to a frame buffer in order to allow post-processing.
|
||||
* This class is intended to provide low-level functions related to WebGL rendering, so that accessing
|
||||
* directly the WebGL API should not be required anymore.
|
||||
*
|
||||
* Several operations are handled by the `WebGLHelper` class:
|
||||
*
|
||||
* ### Define custom shaders and uniforms
|
||||
*
|
||||
* *Shaders* are low-level programs executed on the GPU and written in GLSL. There are two types of shaders:
|
||||
*
|
||||
* Vertex shaders are used to manipulate the position and attribute of *vertices* of rendered primitives (ie. corners of a square).
|
||||
* Outputs are:
|
||||
*
|
||||
* * `gl_Position`: position of the vertex in screen space
|
||||
*
|
||||
* * Varyings usually prefixed with `v_` are passed on to the fragment shader
|
||||
*
|
||||
* Fragment shaders are used to control the actual color of the pixels rawn on screen. Their only output is `gl_FragColor`.
|
||||
*
|
||||
* Both shaders can take *uniforms* or *attributes* as input. Attributes are explained later. Uniforms are common, read-only values that
|
||||
* can be changed at every frame and can be of type float, arrays of float or images.
|
||||
*
|
||||
* 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
|
||||
* const vertexShader = new WebGLVertex(VERTEX_SHADER);
|
||||
* const fragmentShader = new WebGLFragment(FRAGMENT_SHADER);
|
||||
* this.program = this.context.getProgram(fragmentShader, vertexShader);
|
||||
* this.context.useProgram(this.program);
|
||||
* ```
|
||||
*
|
||||
* Uniforms are defined using the `uniforms` option and can either be explicit values or callbacks taking the frame state as argument.
|
||||
* You can also change their value along the way like so:
|
||||
* ```js
|
||||
* this.context.setUniformFloatValue(DefaultUniform.OPACITY, layerState.opacity);
|
||||
* ```
|
||||
*
|
||||
* ### Defining post processing passes
|
||||
*
|
||||
* *Post processing* describes the act of rendering primitives to a texture, and then rendering this texture to the final canvas
|
||||
* while applying special effects in screen space.
|
||||
* Typical uses are: blurring, color manipulation, depth of field, filtering...
|
||||
*
|
||||
* The `WebGLHelper` class offers the possibility to define post processes at creation time using the `postProcesses` option.
|
||||
* A post process step accepts the following options:
|
||||
*
|
||||
* * `fragmentShader` and `vertexShader`: text literals in GLSL language that will be compiled and used in the post processing step.
|
||||
* * `uniforms`: uniforms can be defined for the post processing steps just like for the main render.
|
||||
* * `scaleRatio`: allows using an intermediate texture smaller or higher than the final canvas in the post processing step.
|
||||
* This is typically used in blur steps to reduce the performance overhead by using an already downsampled texture as input.
|
||||
*
|
||||
* The {@link module:ol/webgl/PostProcessingPass~WebGLPostProcessingPass} class is used internally, refer to its documentation for more info.
|
||||
*
|
||||
* ### Binding WebGL buffers and flushing data into them:
|
||||
*
|
||||
* Data that must be passed to the GPU has to be transferred using `WebGLBuffer` objects.
|
||||
* A buffer has to be created only once, but must be bound everytime the data it holds is changed. Using `WebGLHelper.bindBuffer`
|
||||
* will bind the buffer and flush the new data to the GPU.
|
||||
*
|
||||
* For now, the `WebGLHelper` class expects {@link module:ol/webgl/Buffer~WebGLArrayBuffer} objects.
|
||||
* ```js
|
||||
* // at initialization phase
|
||||
* this.verticesBuffer = new WebGLBuffer([], DYNAMIC_DRAW);
|
||||
* this.indicesBuffer = new WebGLBuffer([], DYNAMIC_DRAW);
|
||||
*
|
||||
* // at rendering phase
|
||||
* this.context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer);
|
||||
* this.context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer);
|
||||
* ```
|
||||
*
|
||||
* ### Specifying attributes
|
||||
*
|
||||
* The GPU only receives the data as arrays of numbers. These numbers must be handled differently depending on what it describes (position, texture coordinate...).
|
||||
* Attributes are used to specify these uses. Use `WebGLHelper.enableAttributeArray` and either
|
||||
* the default attribute names in {@link module:ol/webgl/Helper~DefaultAttrib} or custom ones.
|
||||
*
|
||||
* Please note that you will have to specify the type and offset of the attributes in the data array. You can refer to the documentation of [WebGLRenderingContext.vertexAttribPointer](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer) for more explanation.
|
||||
* ```js
|
||||
* // here we indicate that the data array has the following structure:
|
||||
* // [posX, posY, offsetX, offsetY, texCoordU, texCoordV, posX, posY, ...]
|
||||
* let bytesPerFloat = Float32Array.BYTES_PER_ELEMENT;
|
||||
* this.context.enableAttributeArray(DefaultAttrib.POSITION, 2, FLOAT, bytesPerFloat * 6, 0);
|
||||
* this.context.enableAttributeArray(DefaultAttrib.OFFSETS, 2, FLOAT, bytesPerFloat * 6, bytesPerFloat * 2);
|
||||
* this.context.enableAttributeArray(DefaultAttrib.TEX_COORD, 2, FLOAT, bytesPerFloat * 6, bytesPerFloat * 4);
|
||||
* ```
|
||||
*
|
||||
* ### Rendering primitives
|
||||
*
|
||||
* Once all the steps above have been achieved, rendering primitives to the screen is done using `WebGLHelper.prepareDraw` `drawElements` and `finalizeDraw`.
|
||||
* ```js
|
||||
* // frame preparation step
|
||||
* this.context.prepareDraw(frameState);
|
||||
*
|
||||
* // call this for every data array that has to be rendered on screen
|
||||
* this.context.drawElements(0, this.indicesBuffer.getArray().length);
|
||||
*
|
||||
* // finalize the rendering by applying post processes
|
||||
* this.context.finalizeDraw(frameState);
|
||||
* ```
|
||||
*
|
||||
* For an example usage of this class, refer to {@link module:ol/renderer/webgl/PointsLayer~WebGLPointsLayerRenderer}.
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class WebGLHelper extends Disposable {
|
||||
|
||||
/**
|
||||
* @param {Options=} opt_options Options.
|
||||
*/
|
||||
constructor(opt_options) {
|
||||
super();
|
||||
@@ -69,6 +213,7 @@ class WebGLHelper extends Disposable {
|
||||
* @type {WebGLRenderingContext}
|
||||
*/
|
||||
this.gl_ = this.canvas_.getContext('webgl');
|
||||
const gl = this.getGL();
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -101,7 +246,7 @@ class WebGLHelper extends Disposable {
|
||||
|
||||
// use the OES_element_index_uint extension if available
|
||||
if (this.hasOESElementIndexUint) {
|
||||
this.gl_.getExtension('OES_element_index_uint');
|
||||
gl.getExtension('OES_element_index_uint');
|
||||
}
|
||||
|
||||
listen(this.canvas_, ContextEventType.LOST,
|
||||
@@ -146,8 +291,9 @@ class WebGLHelper extends Disposable {
|
||||
this.attribLocations_;
|
||||
|
||||
/**
|
||||
* Holds info about custom uniforms used in the post processing pass
|
||||
* @type {Array<{value: *, texture?: WebGLTexture}>}
|
||||
* Holds info about custom uniforms used in the post processing pass.
|
||||
* If the uniform is a texture, the WebGL Texture object will be stored here.
|
||||
* @type {Array<UniformInternalDescription>}
|
||||
* @private
|
||||
*/
|
||||
this.uniforms_ = [];
|
||||
@@ -158,10 +304,14 @@ class WebGLHelper extends Disposable {
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
// initialize post processes from options
|
||||
// if none given, use a default one
|
||||
const gl = this.getGL();
|
||||
this.postProcessPasses = options.postProcesses ? options.postProcesses.map(function(options) {
|
||||
/**
|
||||
* An array of PostProcessingPass objects is kept in this variable, built from the steps provided in the
|
||||
* options. If no post process was given, a default one is used (so as not to have to make an exception to
|
||||
* the frame buffer logic).
|
||||
* @type {Array<WebGLPostProcessingPass>}
|
||||
* @private
|
||||
*/
|
||||
this.postProcessPasses_ = options.postProcesses ? options.postProcesses.map(function(options) {
|
||||
return new WebGLPostProcessingPass({
|
||||
webGlContext: gl,
|
||||
scaleRatio: options.scaleRatio,
|
||||
@@ -169,7 +319,7 @@ class WebGLHelper extends Disposable {
|
||||
fragmentShader: options.fragmentShader,
|
||||
uniforms: options.uniforms
|
||||
});
|
||||
}) : [new WebGLPostProcessingPass({ webGlContext: gl })];
|
||||
}) : [new WebGLPostProcessingPass({webGlContext: gl})];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +328,8 @@ class WebGLHelper extends Disposable {
|
||||
* the cache.
|
||||
* TODO: improve this, the logic is unclear: we want A/ to bind a buffer and B/ to flush data in it
|
||||
* @param {number} target Target.
|
||||
* @param {WebGLBuffer} buf Buffer.
|
||||
* @param {import("./Buffer").default} buf Buffer.
|
||||
* @api
|
||||
*/
|
||||
bindBuffer(target, buf) {
|
||||
const gl = this.getGL();
|
||||
@@ -236,7 +387,11 @@ class WebGLHelper extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the buffer & set the viewport to draw
|
||||
* Clear the buffer & set the viewport to draw.
|
||||
* Post process passes will be initialized here, the first one being bound as a render target for
|
||||
* subsequent draw calls.
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState current frame state
|
||||
* @api
|
||||
*/
|
||||
prepareDraw(frameState) {
|
||||
const gl = this.getGL();
|
||||
@@ -252,8 +407,8 @@ class WebGLHelper extends Disposable {
|
||||
gl.useProgram(this.currentProgram_);
|
||||
|
||||
// loop backwards in post processes list
|
||||
for (let i = this.postProcessPasses.length - 1; i >= 0; i--) {
|
||||
this.postProcessPasses[i].init(size, pixelRatio);
|
||||
for (let i = this.postProcessPasses_.length - 1; i >= 0; i--) {
|
||||
this.postProcessPasses_[i].init(frameState);
|
||||
}
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
@@ -263,14 +418,15 @@ class WebGLHelper extends Disposable {
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
this.applyFrameState(frameState)
|
||||
this.applyFrameState(frameState);
|
||||
this.applyUniforms(frameState);
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* Execute a draw call based on the currently bound program, texture, buffers, attributes.
|
||||
* @param {number} start Start index.
|
||||
* @param {number} end End index.
|
||||
* @api
|
||||
*/
|
||||
drawElements(start, end) {
|
||||
const gl = this.getGL();
|
||||
@@ -284,17 +440,20 @@ class WebGLHelper extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the frame buffer to the canvas
|
||||
* Apply the successive post process passes which will eventually render to the actual canvas.
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState current frame state
|
||||
* @api
|
||||
*/
|
||||
finalizeDraw(frameState) {
|
||||
// apply post processes using the next one as target
|
||||
for (let i = 0; i < this.postProcessPasses.length; i++) {
|
||||
this.postProcessPasses[i].apply(frameState, this.postProcessPasses[i + 1] || null);
|
||||
for (let i = 0; i < this.postProcessPasses_.length; i++) {
|
||||
this.postProcessPasses_[i].apply(frameState, this.postProcessPasses_[i + 1] || null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {HTMLCanvasElement} Canvas.
|
||||
* @api
|
||||
*/
|
||||
getCanvas() {
|
||||
return this.canvas_;
|
||||
@@ -310,8 +469,9 @@ class WebGLHelper extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the matrices uniforms for a given frame state
|
||||
* Sets the default matrix uniforms for a given frame state. This is called internally in `prepareDraw`.
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @private
|
||||
*/
|
||||
applyFrameState(frameState) {
|
||||
const size = frameState.size;
|
||||
@@ -338,7 +498,11 @@ class WebGLHelper extends Disposable {
|
||||
this.setUniformMatrixValue(DefaultUniform.OFFSET_ROTATION_MATRIX, fromTransform(this.tmpMat4_, offsetRotateMatrix));
|
||||
}
|
||||
|
||||
// todo
|
||||
/**
|
||||
* Sets the custom uniforms based on what was given in the constructor. This is called internally in `prepareDraw`.
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @private
|
||||
*/
|
||||
applyUniforms(frameState) {
|
||||
const gl = this.getGL();
|
||||
|
||||
@@ -380,6 +544,8 @@ class WebGLHelper extends Disposable {
|
||||
case 4:
|
||||
gl.uniform4f(this.getUniformLocation(uniform.name), value[0], value[1], value[2], value[3]);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else if (typeof value === 'number') {
|
||||
gl.uniform1f(this.getUniformLocation(uniform.name), value);
|
||||
@@ -390,8 +556,10 @@ class WebGLHelper extends Disposable {
|
||||
/**
|
||||
* Get shader from the cache if it's in the cache. Otherwise, create
|
||||
* the WebGL shader, compile it, and add entry to cache.
|
||||
* TODO: make compilation errors show up
|
||||
* @param {import("./Shader.js").default} shaderObject Shader object.
|
||||
* @return {WebGLShader} Shader.
|
||||
* @api
|
||||
*/
|
||||
getShader(shaderObject) {
|
||||
const shaderKey = getUid(shaderObject);
|
||||
@@ -402,9 +570,6 @@ class WebGLHelper extends Disposable {
|
||||
const shader = gl.createShader(shaderObject.getType());
|
||||
gl.shaderSource(shader, shaderObject.getSource());
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
console.error(`Shader compilation failed - log:\n${gl.getShaderInfoLog(shader)}`);
|
||||
}
|
||||
this.shaderCache_[shaderKey] = shader;
|
||||
return shader;
|
||||
}
|
||||
@@ -436,6 +601,7 @@ class WebGLHelper extends Disposable {
|
||||
* @param {import("./Fragment.js").default} fragmentShaderObject Fragment shader.
|
||||
* @param {import("./Vertex.js").default} vertexShaderObject Vertex shader.
|
||||
* @return {WebGLProgram} Program.
|
||||
* @api
|
||||
*/
|
||||
getProgram(fragmentShaderObject, vertexShaderObject) {
|
||||
const programKey = getUid(fragmentShaderObject) + '/' + getUid(vertexShaderObject);
|
||||
@@ -456,6 +622,7 @@ class WebGLHelper extends Disposable {
|
||||
* Will get the location from the shader or the cache
|
||||
* @param {string} name Uniform name
|
||||
* @return {WebGLUniformLocation} uniformLocation
|
||||
* @api
|
||||
*/
|
||||
getUniformLocation(name) {
|
||||
if (!this.uniformLocations_[name]) {
|
||||
@@ -468,6 +635,7 @@ class WebGLHelper extends Disposable {
|
||||
* Will get the location from the shader or the cache
|
||||
* @param {string} name Attribute name
|
||||
* @return {number} attribLocation
|
||||
* @api
|
||||
*/
|
||||
getAttributeLocation(name) {
|
||||
if (!this.attribLocations_[name]) {
|
||||
@@ -480,6 +648,7 @@ class WebGLHelper extends Disposable {
|
||||
* Give a value for a standard float uniform
|
||||
* @param {string} uniform Uniform name
|
||||
* @param {number} value Value
|
||||
* @api
|
||||
*/
|
||||
setUniformFloatValue(uniform, value) {
|
||||
this.getGL().uniform1f(this.getUniformLocation(uniform), value);
|
||||
@@ -489,6 +658,7 @@ class WebGLHelper extends Disposable {
|
||||
* Give a value for a standard matrix4 uniform
|
||||
* @param {string} uniform Uniform name
|
||||
* @param {Array<number>} value Matrix value
|
||||
* @api
|
||||
*/
|
||||
setUniformMatrixValue(uniform, value) {
|
||||
this.getGL().uniformMatrix4fv(this.getUniformLocation(uniform), false, value);
|
||||
@@ -496,11 +666,12 @@ class WebGLHelper extends Disposable {
|
||||
|
||||
/**
|
||||
* Will set the currently bound buffer to an attribute of the shader program
|
||||
* @param {string} attribName
|
||||
* @param {string} attribName Attribute name
|
||||
* @param {number} size Number of components per attributes
|
||||
* @param {number} type UNSIGNED_INT, UNSIGNED_BYTE, UNSIGNED_SHORT or FLOAT
|
||||
* @param {number} stride Stride in bytes (0 means attribs are packed)
|
||||
* @param {number} offset Offset in bytes
|
||||
* @api
|
||||
*/
|
||||
enableAttributeArray(attribName, size, type, stride, offset) {
|
||||
this.getGL().enableVertexAttribArray(this.getAttributeLocation(attribName));
|
||||
@@ -509,7 +680,8 @@ class WebGLHelper extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
* WebGL context was lost
|
||||
* @private
|
||||
*/
|
||||
handleWebGLContextLost() {
|
||||
clear(this.bufferCache_);
|
||||
@@ -519,7 +691,8 @@ class WebGLHelper extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
* WebGL context was restored
|
||||
* @private
|
||||
*/
|
||||
handleWebGLContextRestored() {
|
||||
}
|
||||
@@ -527,6 +700,7 @@ class WebGLHelper extends Disposable {
|
||||
// TODO: shutdown program
|
||||
|
||||
/**
|
||||
* TODO: these are not used and should be reworked
|
||||
* @param {number=} opt_wrapS wrapS.
|
||||
* @param {number=} opt_wrapT wrapT.
|
||||
* @return {WebGLTexture} The texture.
|
||||
@@ -551,6 +725,7 @@ class WebGLHelper extends Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: these are not used and should be reworked
|
||||
* @param {number} width Width.
|
||||
* @param {number} height Height.
|
||||
* @param {number=} opt_wrapS wrapS.
|
||||
@@ -559,13 +734,14 @@ class WebGLHelper extends Disposable {
|
||||
*/
|
||||
createEmptyTexture(width, height, opt_wrapS, opt_wrapT) {
|
||||
const gl = this.getGL();
|
||||
const texture = this.createTextureInternal( opt_wrapS, opt_wrapT);
|
||||
const texture = this.createTextureInternal(opt_wrapS, opt_wrapT);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO: these are not used and should be reworked
|
||||
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image.
|
||||
* @param {number=} opt_wrapS wrapS.
|
||||
* @param {number=} opt_wrapT wrapT.
|
||||
|
||||
@@ -31,10 +31,70 @@ const DEFAULT_FRAGMENT_SHADER = `
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {WebGLContext} webGlContext WebGL context; mandatory.
|
||||
* @property {number} [scaleRatio] Scale ratio; if < 1, the post process will render to a texture smaller than
|
||||
* the main canvas that will then be sampled up (useful for saving resource on blur steps).
|
||||
* @property {string} [vertexShader] Vertex shader source
|
||||
* @property {string} [fragmentShader] Fragment shader source
|
||||
* @property {Object.<string,import("./Helper").UniformValue>} [uniforms] Uniform definitions for the post process step
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} UniformInternalDescription
|
||||
* @property {UniformValue} value Value
|
||||
* @property {number} location Location
|
||||
* @property {WebGLTexture} [texture] Texture
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* This class is used to define Post Processing passes with custom shaders and uniforms.
|
||||
* This is used internally by {@link module:ol/webgl/Helper~WebGLHelper}.
|
||||
*
|
||||
* Default shaders are shown hereafter:
|
||||
*
|
||||
* * Vertex shader:
|
||||
*
|
||||
* ```
|
||||
* precision mediump float;
|
||||
*
|
||||
* attribute vec2 a_position;
|
||||
* varying vec2 v_texCoord;
|
||||
* varying vec2 v_screenCoord;
|
||||
*
|
||||
* uniform vec2 u_screenSize;
|
||||
*
|
||||
* void main() {
|
||||
* v_texCoord = a_position * 0.5 + 0.5;
|
||||
* v_screenCoord = v_texCoord * u_screenSize;
|
||||
* gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* * Fragment shader:
|
||||
*
|
||||
* ```
|
||||
* precision mediump float;
|
||||
*
|
||||
* uniform sampler2D u_image;
|
||||
*
|
||||
* varying vec2 v_texCoord;
|
||||
* varying vec2 v_screenCoord;
|
||||
*
|
||||
* void main() {
|
||||
* gl_FragColor = texture2D(u_image, v_texCoord);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class WebGLPostProcessingPass {
|
||||
|
||||
/**
|
||||
* // todo
|
||||
* @param {Options=} options Options.
|
||||
*/
|
||||
constructor(options) {
|
||||
this.gl_ = options.webGlContext;
|
||||
@@ -48,18 +108,13 @@ class WebGLPostProcessingPass {
|
||||
this.frameBuffer_ = gl.createFramebuffer();
|
||||
|
||||
// compile the program for the frame buffer
|
||||
// TODO: make compilation errors show up
|
||||
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
gl.shaderSource(vertexShader, options.vertexShader || DEFAULT_VERTEX_SHADER);
|
||||
gl.compileShader(vertexShader);
|
||||
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
|
||||
console.error(`Shader compilation failed - log:\n${gl.getShaderInfoLog(vertexShader)}`);
|
||||
}
|
||||
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
gl.shaderSource(fragmentShader, options.fragmentShader || DEFAULT_FRAGMENT_SHADER);
|
||||
gl.compileShader(fragmentShader);
|
||||
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
|
||||
console.error(`Shader compilation failed - log:\n${gl.getShaderInfoLog(fragmentShader)}`);
|
||||
}
|
||||
this.renderTargetProgram_ = gl.createProgram();
|
||||
gl.attachShader(this.renderTargetProgram_, vertexShader);
|
||||
gl.attachShader(this.renderTargetProgram_, fragmentShader);
|
||||
@@ -84,7 +139,7 @@ class WebGLPostProcessingPass {
|
||||
|
||||
/**
|
||||
* Holds info about custom uniforms used in the post processing pass
|
||||
* @type {Array<{value: *, location: WebGLUniformLocation, texture?: WebGLTexture}>}
|
||||
* @type {Array<UniformInternalDescription>}
|
||||
* @private
|
||||
*/
|
||||
this.uniforms_ = [];
|
||||
@@ -105,11 +160,17 @@ class WebGLPostProcessingPass {
|
||||
return this.gl_;
|
||||
}
|
||||
|
||||
// todo
|
||||
// the last postprocess initialized will be the one where the primitives are drawn
|
||||
init(size) {
|
||||
/**
|
||||
* Initialize the render target texture of the post process, make sure it is at the
|
||||
* right size and bind it as a render target for the next draw calls.
|
||||
* The last step to be initialized will be the one where the primitives are rendered.
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState current frame state
|
||||
* @api
|
||||
*/
|
||||
init(frameState) {
|
||||
const gl = this.getGL();
|
||||
const canvas = gl.canvas;
|
||||
const size = frameState.size;
|
||||
|
||||
// rendering goes to my buffer
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.getFrameBuffer());
|
||||
@@ -141,8 +202,12 @@ class WebGLPostProcessingPass {
|
||||
}
|
||||
}
|
||||
|
||||
// todo
|
||||
// render to the next postprocessing pass (or to the canvas if final pass)
|
||||
/**
|
||||
* Render to the next postprocessing pass (or to the canvas if final pass).
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState current frame state
|
||||
* @param {WebGLPostProcessingPass} [nextPass] Next pass, optional
|
||||
* @api
|
||||
*/
|
||||
apply(frameState, nextPass) {
|
||||
const gl = this.getGL();
|
||||
const canvas = gl.canvas;
|
||||
@@ -171,12 +236,19 @@ class WebGLPostProcessingPass {
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
// todo
|
||||
/**
|
||||
* @returns {WebGLFramebuffer} Frame buffer
|
||||
* @api
|
||||
*/
|
||||
getFrameBuffer() {
|
||||
return this.frameBuffer_;
|
||||
}
|
||||
|
||||
// todo
|
||||
/**
|
||||
* Sets the custom uniforms based on what was given in the constructor.
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @private
|
||||
*/
|
||||
applyUniforms(frameState) {
|
||||
const gl = this.getGL();
|
||||
|
||||
@@ -218,6 +290,7 @@ class WebGLPostProcessingPass {
|
||||
case 4:
|
||||
gl.uniform4f(uniform.location, value[0], value[1], value[2], value[3]);
|
||||
return;
|
||||
default: return;
|
||||
}
|
||||
} else if (typeof value === 'number') {
|
||||
gl.uniform1f(uniform.location, value);
|
||||
|
||||
Reference in New Issue
Block a user