From 79c974d63d2b4bdad1857768dbaa5d2ce7cb8438 Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Thu, 9 Jun 2022 13:04:37 +0200 Subject: [PATCH] WebGL / Add new module with default shaders for VectorLayer --- src/ol/renderer/webgl/VectorLayer.js | 1 + src/ol/renderer/webgl/shaders.js | 213 +++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 src/ol/renderer/webgl/shaders.js diff --git a/src/ol/renderer/webgl/VectorLayer.js b/src/ol/renderer/webgl/VectorLayer.js index ba8dcd42c5..0165264cf4 100644 --- a/src/ol/renderer/webgl/VectorLayer.js +++ b/src/ol/renderer/webgl/VectorLayer.js @@ -15,6 +15,7 @@ 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 diff --git a/src/ol/renderer/webgl/shaders.js b/src/ol/renderer/webgl/shaders.js new file mode 100644 index 0000000000..8acea1fd19 --- /dev/null +++ b/src/ol/renderer/webgl/shaders.js @@ -0,0 +1,213 @@ +/** + * @module ol/renderer/webgl/shaders + */ +import {asArray} from '../../color.js'; + +/** + * Attribute names used in the default shaders. + * @enum {string} + */ +export const DefaultAttributes = { + COLOR: 'color', + OPACITY: 'opacity', + WIDTH: 'width', +}; + +/** + * Packs red/green/blue channels of a color into a single float value; alpha is ignored. + * This is how DefaultAttributes.COLOR is expected to be computed. + * @param {import("../../color.js").Color|string} color Color as array of numbers or string + * @return {number} Float value containing the color + * @api + */ +export function packColor(color) { + const array = asArray(color); + const r = array[0] * 256 * 256; + const g = array[1] * 256; + const b = array[2]; + return r + g + b; +} + +const DECODE_COLOR_EXPRESSION = `vec3( + fract(floor(a_color / 256.0 / 256.0) / 256.0), + fract(floor(a_color / 256.0) / 256.0), + fract(a_color / 256.0) +);`; + +/** + * Default polygon vertex shader. + * Relies on DefaultAttributes.COLOR and DefaultAttributes.OPACITY. + * @type {string} + * @api + */ +export const DEFAULT_POLYGON_VERTEX = ` + precision mediump float; + uniform mat4 u_projectionMatrix; + attribute vec2 a_position; + attribute float a_color; + attribute float a_opacity; + varying vec3 v_color; + varying float v_opacity; + + void main(void) { + gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0); + v_color = ${DECODE_COLOR_EXPRESSION} + v_opacity = a_opacity; + }`; + +/** + * Default polygon fragment shader. + * @type {string} + * @api + */ +export const DEFAULT_POLYGON_FRAGMENT = ` + precision mediump float; + varying vec3 v_color; + varying float v_opacity; + + void main(void) { + gl_FragColor = vec4(v_color, 1.0) * v_opacity; + }`; + +/** + * Default linestring vertex shader. + * Relies on DefaultAttributes.COLOR, DefaultAttributes.OPACITY and DefaultAttributes.WIDTH. + * @type {string} + * @api + */ +export const DEFAULT_LINESTRING_VERTEX = ` + 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; + attribute float a_opacity; + attribute float a_width; + varying vec2 v_segmentStart; + varying vec2 v_segmentEnd; + varying float v_angleStart; + varying float v_angleEnd; + varying vec3 v_color; + varying float v_opacity; + varying float v_width; + + 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; + } + + 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) * a_width * 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 = ${DECODE_COLOR_EXPRESSION} + v_opacity = a_opacity; + v_width = a_width; + }`; + +/** + * Default linestring fragment shader. + * @type {string} + * @api + */ +export const DEFAULT_LINESTRING_FRAGMENT = ` + 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; + varying float v_opacity; + varying float v_width; + + 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); + } + + void main(void) { + vec2 v_currentPoint = gl_FragCoord.xy / u_pixelRatio; + gl_FragColor = vec4(v_color, 1.0) * v_opacity; + gl_FragColor *= segmentDistanceField(v_currentPoint, v_segmentStart, v_segmentEnd, v_width); + }`; + +/** + * Default point vertex shader. + * Relies on DefaultAttributes.COLOR and DefaultAttributes.OPACITY. + * @type {string} + * @api + */ +export const DEFAULT_POINT_VERTEX = ` + precision mediump float; + uniform mat4 u_projectionMatrix; + uniform mat4 u_offsetScaleMatrix; + attribute vec2 a_position; + attribute float a_index; + attribute float a_color; + attribute float a_opacity; + varying vec2 v_texCoord; + varying vec3 v_color; + varying float v_opacity; + + 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); + v_color = ${DECODE_COLOR_EXPRESSION} + v_opacity = a_opacity; + }`; + +/** + * Default point fragment shader. + * @type {string} + * @api + */ +export const DEFAULT_POINT_FRAGMENT = ` + precision mediump float; + varying vec3 v_color; + varying float v_opacity; + + void main(void) { + gl_FragColor = vec4(v_color, 1.0) * v_opacity; + }`;