WebGL / Reorganize VectorLayerRenderer options, update example
Now different attributes can be provided for each type of geometry. Also updated the example to accomodate for this and use the default shaders.
This commit is contained in:
@@ -6,159 +6,43 @@ import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||
import VectorSource from '../src/ol/source/Vector.js';
|
||||
import View from '../src/ol/View.js';
|
||||
import WebGLVectorLayerRenderer from '../src/ol/renderer/webgl/VectorLayer.js';
|
||||
import {
|
||||
DefaultAttributes,
|
||||
packColor,
|
||||
} from '../src/ol/renderer/webgl/shaders.js';
|
||||
import {asArray} from '../src/ol/color.js';
|
||||
|
||||
class WebGLLayer extends Layer {
|
||||
createRenderer() {
|
||||
return new WebGLVectorLayerRenderer(this, {
|
||||
className: this.getClassName(),
|
||||
polygonVertexShader: `
|
||||
precision mediump float;
|
||||
uniform mat4 u_projectionMatrix;
|
||||
attribute vec2 a_position;
|
||||
attribute float a_color;
|
||||
|
||||
varying vec3 v_color;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0);
|
||||
v_color = vec3(
|
||||
floor(a_color / 256.0 / 256.0) / 256.0,
|
||||
fract(floor(a_color / 256.0) / 256.0),
|
||||
fract(a_color / 256.0)
|
||||
);
|
||||
}`,
|
||||
polygonFragmentShader: `
|
||||
precision mediump float;
|
||||
varying vec3 v_color;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(v_color.rgb, 1.0);
|
||||
gl_FragColor *= 0.75;
|
||||
}`,
|
||||
lineStringVertexShader: `
|
||||
precision mediump float;
|
||||
uniform mat4 u_projectionMatrix;
|
||||
uniform vec2 u_sizePx;
|
||||
attribute vec2 a_segmentStart;
|
||||
attribute vec2 a_segmentEnd;
|
||||
attribute float a_parameters;
|
||||
attribute float a_color;
|
||||
varying vec2 v_segmentStart;
|
||||
varying vec2 v_segmentEnd;
|
||||
varying float v_angleStart;
|
||||
varying float v_angleEnd;
|
||||
|
||||
varying vec3 v_color;
|
||||
|
||||
vec2 worldToPx(vec2 worldPos) {
|
||||
vec4 screenPos = u_projectionMatrix * vec4(worldPos, 0.0, 1.0);
|
||||
return (0.5 * screenPos.xy + 0.5) * u_sizePx;
|
||||
}
|
||||
|
||||
vec4 pxToScreen(vec2 pxPos) {
|
||||
vec2 screenPos = pxPos * 4.0 / u_sizePx;
|
||||
return vec4(screenPos.xy, 0.0, 0.0);
|
||||
}
|
||||
|
||||
vec2 getOffsetDirection(vec2 normalPx, vec2 tangentPx, float joinAngle) {
|
||||
if (cos(joinAngle) > 0.93) return normalPx - tangentPx;
|
||||
float halfAngle = joinAngle / 2.0;
|
||||
vec2 angleBisectorNormal = vec2(
|
||||
sin(halfAngle) * normalPx.x + cos(halfAngle) * normalPx.y,
|
||||
-cos(halfAngle) * normalPx.x + sin(halfAngle) * normalPx.y
|
||||
);
|
||||
float length = 1.0 / sin(halfAngle);
|
||||
return angleBisectorNormal * length;
|
||||
}
|
||||
|
||||
float lineWidth = 2.0;
|
||||
|
||||
void main(void) {
|
||||
float anglePrecision = 1500.0;
|
||||
float paramShift = 10000.0;
|
||||
v_angleStart = fract(a_parameters / paramShift) * paramShift / anglePrecision;
|
||||
v_angleEnd = fract(floor(a_parameters / paramShift + 0.5) / paramShift) * paramShift / anglePrecision;
|
||||
float vertexNumber = floor(a_parameters / paramShift / paramShift + 0.0001);
|
||||
vec2 tangentPx = worldToPx(a_segmentEnd) - worldToPx(a_segmentStart);
|
||||
tangentPx = normalize(tangentPx);
|
||||
vec2 normalPx = vec2(-tangentPx.y, tangentPx.x);
|
||||
float normalDir = vertexNumber < 0.5 || (vertexNumber > 1.5 && vertexNumber < 2.5) ? 1.0 : -1.0;
|
||||
float tangentDir = vertexNumber < 1.5 ? 1.0 : -1.0;
|
||||
float angle = vertexNumber < 1.5 ? v_angleStart : v_angleEnd;
|
||||
vec2 offsetPx = getOffsetDirection(normalPx * normalDir, tangentDir * tangentPx, angle) * lineWidth * 0.5;
|
||||
vec2 position = vertexNumber < 1.5 ? a_segmentStart : a_segmentEnd;
|
||||
gl_Position = u_projectionMatrix * vec4(position, 0.0, 1.0) + pxToScreen(offsetPx);
|
||||
v_segmentStart = worldToPx(a_segmentStart);
|
||||
v_segmentEnd = worldToPx(a_segmentEnd);
|
||||
|
||||
v_color = vec3(
|
||||
floor(a_color / 256.0 / 256.0) / 256.0,
|
||||
fract(floor(a_color / 256.0) / 256.0),
|
||||
fract(a_color / 256.0)
|
||||
);
|
||||
}`,
|
||||
lineStringFragmentShader: `
|
||||
precision mediump float;
|
||||
uniform float u_pixelRatio;
|
||||
varying vec2 v_segmentStart;
|
||||
varying vec2 v_segmentEnd;
|
||||
varying float v_angleStart;
|
||||
varying float v_angleEnd;
|
||||
|
||||
varying vec3 v_color;
|
||||
|
||||
float segmentDistanceField(vec2 point, vec2 start, vec2 end, float radius) {
|
||||
vec2 startToPoint = point - start;
|
||||
vec2 startToEnd = end - start;
|
||||
float ratio = clamp(dot(startToPoint, startToEnd) / dot(startToEnd, startToEnd), 0.0, 1.0);
|
||||
float dist = length(startToPoint - ratio * startToEnd);
|
||||
return 1.0 - smoothstep(radius - 1.0, radius, dist);
|
||||
}
|
||||
|
||||
float lineWidth = 1.5;
|
||||
|
||||
void main(void) {
|
||||
vec2 v_currentPoint = gl_FragCoord.xy / u_pixelRatio;
|
||||
gl_FragColor = vec4(v_color.rgb * 0.75, 1.0);
|
||||
gl_FragColor *= segmentDistanceField(v_currentPoint, v_segmentStart, v_segmentEnd, lineWidth);
|
||||
}`,
|
||||
pointVertexShader: `
|
||||
precision mediump float;
|
||||
uniform mat4 u_projectionMatrix;
|
||||
uniform mat4 u_offsetScaleMatrix;
|
||||
attribute vec2 a_position;
|
||||
attribute float a_index;
|
||||
varying vec2 v_texCoord;
|
||||
|
||||
void main(void) {
|
||||
mat4 offsetMatrix = u_offsetScaleMatrix;
|
||||
float size = 6.0;
|
||||
float offsetX = a_index == 0.0 || a_index == 3.0 ? -size / 2.0 : size / 2.0;
|
||||
float offsetY = a_index == 0.0 || a_index == 1.0 ? -size / 2.0 : size / 2.0;
|
||||
vec4 offsets = offsetMatrix * vec4(offsetX, offsetY, 0.0, 0.0);
|
||||
gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;
|
||||
float u = a_index == 0.0 || a_index == 3.0 ? 0.0 : 1.0;
|
||||
float v = a_index == 0.0 || a_index == 1.0 ? 0.0 : 1.0;
|
||||
v_texCoord = vec2(u, v);
|
||||
}`,
|
||||
pointFragmentShader: `
|
||||
precision mediump float;
|
||||
varying vec2 v_texCoord;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}`,
|
||||
attributes: [
|
||||
{
|
||||
name: 'color',
|
||||
callback: function (feature, properties) {
|
||||
polygonShader: {
|
||||
attributes: {
|
||||
[DefaultAttributes.COLOR]: function (feature, properties) {
|
||||
const color = asArray(properties.COLOR || '#eee');
|
||||
// RGB components are encoded into one value
|
||||
return color[0] * 256 * 256 + color[1] * 256 + color[2];
|
||||
color[3] = 0.85;
|
||||
return packColor(color);
|
||||
},
|
||||
[DefaultAttributes.OPACITY]: function () {
|
||||
return 0.6;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
lineStringShader: {
|
||||
attributes: {
|
||||
[DefaultAttributes.COLOR]: function (feature, properties) {
|
||||
const color = [...asArray(properties.COLOR || '#eee')];
|
||||
color.forEach((_, i) => (color[i] = Math.round(color[i] * 0.75))); // darken slightly
|
||||
return packColor(color);
|
||||
},
|
||||
[DefaultAttributes.WIDTH]: function () {
|
||||
return 1.5;
|
||||
},
|
||||
[DefaultAttributes.OPACITY]: function () {
|
||||
return 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,39 +10,44 @@ import PolygonBatchRenderer from '../../render/webgl/PolygonBatchRenderer.js';
|
||||
import VectorEventType from '../../source/VectorEventType.js';
|
||||
import ViewHint from '../../ViewHint.js';
|
||||
import WebGLLayerRenderer from './Layer.js';
|
||||
import {
|
||||
DEFAULT_LINESTRING_FRAGMENT,
|
||||
DEFAULT_LINESTRING_VERTEX,
|
||||
DEFAULT_POINT_FRAGMENT,
|
||||
DEFAULT_POINT_VERTEX,
|
||||
DEFAULT_POLYGON_FRAGMENT,
|
||||
DEFAULT_POLYGON_VERTEX,
|
||||
DefaultAttributes,
|
||||
packColor,
|
||||
} from './shaders.js';
|
||||
import {DefaultUniform} from '../../webgl/Helper.js';
|
||||
import {buffer, createEmpty, equals, getWidth} from '../../extent.js';
|
||||
import {create as createTransform} from '../../transform.js';
|
||||
import {create as createWebGLWorker} from '../../worker/webgl.js';
|
||||
import {listen, unlistenByKey} from '../../events.js';
|
||||
import './shaders.js'; // this is to make sure that default shaders are part of the bundle
|
||||
|
||||
/**
|
||||
* @typedef {Object} CustomAttribute A description of a custom attribute to be passed on to the GPU, with a value different
|
||||
* for each feature.
|
||||
* @property {string} name Attribute name.
|
||||
* @property {function(import("../../Feature").default, Object<string, *>):number} callback This callback computes the numerical value of the
|
||||
* attribute for a given feature (properties are available as 2nd arg for quicker access).
|
||||
* @typedef {function(import("../../Feature").default, Object<string, *>):number} CustomAttributeCallback A callback computing
|
||||
* the value of a custom attribute (different for each feature) to be passed on to the GPU.
|
||||
* Properties are available as 2nd arg for quicker access.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ShaderProgram An object containing both shaders (vertex and fragment) as well as the required attributes
|
||||
* @property {string} [vertexShader] Vertex shader source (using the default one if unspecified).
|
||||
* @property {string} [fragmentShader] Fragment shader source (using the default one if unspecified).
|
||||
* @property {Object<string,CustomAttributeCallback>} attributes Custom attributes made available in the vertex shader.
|
||||
* Keys are the names of the attributes which are then accessible in the vertex shader using the `a_` prefix, e.g.: `a_opacity`.
|
||||
* Default shaders rely on the attributes in {@link module:ol/render/webgl/shaders~DefaultAttributes}.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {string} [className='ol-layer'] A CSS class name to set to the canvas element.
|
||||
* @property {Array<CustomAttribute>} [attributes] These attributes will be read from the features in the source
|
||||
* and then passed to the GPU. The `name` property of each attribute will serve as its identifier:
|
||||
* * In the vertex shader as an `attribute` by prefixing it with `a_`
|
||||
* * In the fragment shader as a `varying` by prefixing it with `v_`
|
||||
* Please note that these can only be numerical values.
|
||||
* @property {string} polygonVertexShader Vertex shader source, mandatory.
|
||||
* @property {string} polygonFragmentShader Fragment shader source, mandatory.
|
||||
* @property {string} lineStringVertexShader Vertex shader source, mandatory.
|
||||
* @property {string} lineStringFragmentShader Fragment shader source, mandatory.
|
||||
* @property {string} pointVertexShader Vertex shader source, mandatory.
|
||||
* @property {string} pointFragmentShader Fragment shader source, mandatory.
|
||||
* @property {string} [hitVertexShader] Vertex shader source for hit detection rendering.
|
||||
* @property {string} [hitFragmentShader] Fragment shader source for hit detection rendering.
|
||||
* @property {Object<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions for the post process steps
|
||||
* Please note that `u_texture` is reserved for the main texture slot.
|
||||
* @property {ShaderProgram} [polygonShader] Vertex shaders for polygons; using default shader if unspecified
|
||||
* @property {ShaderProgram} [lineStringShader] Vertex shaders for line strings; using default shader if unspecified
|
||||
* @property {ShaderProgram} [pointShader] Vertex shaders for points; using default shader if unspecified
|
||||
* @property {Object<string,import("../../webgl/Helper").UniformValue>} [uniforms] Uniform definitions.
|
||||
* @property {Array<import("./Layer").PostProcessesOptions>} [postProcesses] Post-processes definitions
|
||||
*/
|
||||
|
||||
@@ -119,13 +124,63 @@ class WebGLVectorLayerRenderer extends WebGLLayerRenderer {
|
||||
*/
|
||||
this.currentTransform_ = projectionMatrixTransform;
|
||||
|
||||
this.polygonVertexShader_ = options.polygonVertexShader;
|
||||
this.polygonFragmentShader_ = options.polygonFragmentShader;
|
||||
this.pointVertexShader_ = options.pointVertexShader;
|
||||
this.pointFragmentShader_ = options.pointFragmentShader;
|
||||
this.lineStringVertexShader_ = options.lineStringVertexShader;
|
||||
this.lineStringFragmentShader_ = options.lineStringFragmentShader;
|
||||
this.attributes_ = options.attributes;
|
||||
const polygonAttributesWithDefault = {
|
||||
[DefaultAttributes.COLOR]: function () {
|
||||
return packColor('#ddd');
|
||||
},
|
||||
[DefaultAttributes.OPACITY]: function () {
|
||||
return 1;
|
||||
},
|
||||
...(options.polygonShader && options.polygonShader.attributes),
|
||||
};
|
||||
const lineAttributesWithDefault = {
|
||||
[DefaultAttributes.COLOR]: function () {
|
||||
return packColor('#eee');
|
||||
},
|
||||
[DefaultAttributes.OPACITY]: function () {
|
||||
return 1;
|
||||
},
|
||||
[DefaultAttributes.WIDTH]: function () {
|
||||
return 1.5;
|
||||
},
|
||||
...(options.lineStringShader && options.lineStringShader.attributes),
|
||||
};
|
||||
const pointAttributesWithDefault = {
|
||||
[DefaultAttributes.COLOR]: function () {
|
||||
return packColor('#eee');
|
||||
},
|
||||
[DefaultAttributes.OPACITY]: function () {
|
||||
return 1;
|
||||
},
|
||||
...(options.pointShader && options.pointShader.attributes),
|
||||
};
|
||||
function toAttributesArray(obj) {
|
||||
return Object.keys(obj).map((key) => ({name: key, callback: obj[key]}));
|
||||
}
|
||||
|
||||
this.polygonVertexShader_ =
|
||||
(options.polygonShader && options.polygonShader.vertexShader) ||
|
||||
DEFAULT_POLYGON_VERTEX;
|
||||
this.polygonFragmentShader_ =
|
||||
(options.polygonShader && options.polygonShader.fragmentShader) ||
|
||||
DEFAULT_POLYGON_FRAGMENT;
|
||||
this.polygonAttributes_ = toAttributesArray(polygonAttributesWithDefault);
|
||||
|
||||
this.lineStringVertexShader_ =
|
||||
(options.lineStringShader && options.lineStringShader.vertexShader) ||
|
||||
DEFAULT_LINESTRING_VERTEX;
|
||||
this.lineStringFragmentShader_ =
|
||||
(options.lineStringShader && options.lineStringShader.fragmentShader) ||
|
||||
DEFAULT_LINESTRING_FRAGMENT;
|
||||
this.lineStringAttributes_ = toAttributesArray(lineAttributesWithDefault);
|
||||
|
||||
this.pointVertexShader_ =
|
||||
(options.pointShader && options.pointShader.vertexShader) ||
|
||||
DEFAULT_POINT_VERTEX;
|
||||
this.pointFragmentShader_ =
|
||||
(options.pointShader && options.pointShader.fragmentShader) ||
|
||||
DEFAULT_POINT_FRAGMENT;
|
||||
this.pointAttributes_ = toAttributesArray(pointAttributesWithDefault);
|
||||
|
||||
this.worker_ = createWebGLWorker();
|
||||
|
||||
@@ -167,21 +222,21 @@ class WebGLVectorLayerRenderer extends WebGLLayerRenderer {
|
||||
this.worker_,
|
||||
this.polygonVertexShader_,
|
||||
this.polygonFragmentShader_,
|
||||
this.attributes_ || []
|
||||
this.polygonAttributes_
|
||||
);
|
||||
this.pointRenderer_ = new PointBatchRenderer(
|
||||
this.helper,
|
||||
this.worker_,
|
||||
this.pointVertexShader_,
|
||||
this.pointFragmentShader_,
|
||||
this.attributes_ || []
|
||||
this.pointAttributes_
|
||||
);
|
||||
this.lineStringRenderer_ = new LineStringBatchRenderer(
|
||||
this.helper,
|
||||
this.worker_,
|
||||
this.lineStringVertexShader_,
|
||||
this.lineStringFragmentShader_,
|
||||
this.attributes_ || []
|
||||
this.lineStringAttributes_
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user