From 979dfd3a5522af8cc849cce25240287faf4f742a Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Wed, 9 Mar 2022 23:34:17 +0100 Subject: [PATCH] Add an example for the new webgl vector layer This example accomodates for hit detection whih is not functional yet. --- examples/webgl-vector-layer.html | 10 ++ examples/webgl-vector-layer.js | 182 +++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 examples/webgl-vector-layer.html create mode 100644 examples/webgl-vector-layer.js diff --git a/examples/webgl-vector-layer.html b/examples/webgl-vector-layer.html new file mode 100644 index 0000000000..f297fba476 --- /dev/null +++ b/examples/webgl-vector-layer.html @@ -0,0 +1,10 @@ +--- +layout: example.html +title: WebGL Vector Layer +shortdesc: Example of a vector layer rendered using WebGL +docs: > + The ecoregions are loaded from a GeoJSON file. Information about ecoregions is shown on hover and click. +tags: "vector, geojson, webgl" +--- +
+
 
diff --git a/examples/webgl-vector-layer.js b/examples/webgl-vector-layer.js new file mode 100644 index 0000000000..3b8672d826 --- /dev/null +++ b/examples/webgl-vector-layer.js @@ -0,0 +1,182 @@ +import GeoJSON from '../src/ol/format/GeoJSON.js'; +import Layer from '../src/ol/layer/Layer.js'; +import Map from '../src/ol/Map.js'; +import OSM from '../src/ol/source/OSM.js'; +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 {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; + 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) { + gl_FragColor = vec4(v_color.rgb * 0.75, 1.0); + gl_FragColor *= segmentDistanceField(gl_FragCoord.xy, 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) { + const color = asArray(properties.COLOR || '#eee'); + // RGB components are encoded into one value + return color[0] * 256 * 256 + color[1] * 256 + color[2]; + }, + }, + ], + }); + } +} + +const osm = new TileLayer({ + source: new OSM(), +}); + +const vectorLayer = new WebGLLayer({ + source: new VectorSource({ + url: 'https://openlayers.org/data/vector/ecoregions.json', + format: new GeoJSON(), + }), +}); + +const map = new Map({ + layers: [osm, vectorLayer], + target: 'map', + view: new View({ + center: [0, 0], + zoom: 1, + }), +});