From 921a907386a83a159cdece3f224ea3c31bce3b53 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Fri, 27 Jan 2023 14:39:00 -0800 Subject: [PATCH 1/7] PoC simplification to globe custom layer interface --- debug/satellites-custom-layer.js | 39 ++++++--------------- src/render/draw_custom.js | 14 ++++++-- src/style/style_layer/custom_style_layer.js | 28 +++++++++++++++ src/ui/map.js | 5 +++ 4 files changed, 56 insertions(+), 30 deletions(-) diff --git a/debug/satellites-custom-layer.js b/debug/satellites-custom-layer.js index 243d1604679..ae2895c56b4 100644 --- a/debug/satellites-custom-layer.js +++ b/debug/satellites-custom-layer.js @@ -5,27 +5,9 @@ const globeVertCode = ` attribute vec3 a_pos_ecef; attribute vec3 a_pos_merc; - uniform mat4 u_projection; - uniform mat4 u_globeToMercMatrix; - uniform float u_globeToMercatorTransition; - uniform vec2 u_centerInMerc; - uniform float u_pixelsPerMeterRatio; - void main() { - vec4 p = u_projection * u_globeToMercMatrix * vec4(a_pos_ecef, 1.); - p /= p.w; - if (u_globeToMercatorTransition > 0.) { - - vec4 merc = vec4(a_pos_merc, 1.); - merc.xy = (merc.xy - u_centerInMerc) * u_pixelsPerMeterRatio + u_centerInMerc; - merc.z *= u_pixelsPerMeterRatio; - - merc = u_projection * merc; - merc /= merc.w; - p = mix(p, merc, u_globeToMercatorTransition); - } gl_PointSize = 30.; - gl_Position = p; + gl_Position = project_custom_layer(a_pos_ecef, a_pos_merc); } `; @@ -100,7 +82,7 @@ const satellitesLayer = { this.posEcefVbo = gl.createBuffer(); this.posMercVbo = gl.createBuffer(); - this.globeProgram = createProgram(gl, globeVertCode, fragCode); + this.globeProgram = createProgram(gl, map.globeCustomLayerVertexHeader.concat(globeVertCode), fragCode); this.mercProgram = createProgram(gl, mercVertCode, fragCode); fetch('space-track-leo.txt').then(r => r.text()).then(rawData => { @@ -143,25 +125,26 @@ const satellitesLayer = { } }, - render (gl, projectionMatrix, projection, globeToMercMatrix, transition, centerInMercator, pixelsPerMeterRatio) { + getShaderProgram (projection) { + return projection && projection.name === 'globe' ? this.globeProgram : this.mercProgram; + }, + + render (gl, projectionMatrix, projection) { if (this.satData) { this.updateBuffers(); const primitiveCount = this.posEcef.length / 3; gl.disable(gl.DEPTH_TEST); - if (projection && projection.name === 'globe') { // globe projection and globe to mercator transition + if (projection && projection.name === 'globe') { + // globe projection and globe to mercator transition gl.useProgram(this.globeProgram); updateVboAndActivateAttrib(gl, this.globeProgram, this.posEcefVbo, this.posEcef, "a_pos_ecef"); updateVboAndActivateAttrib(gl, this.globeProgram, this.posMercVbo, this.posMerc, "a_pos_merc"); - gl.uniformMatrix4fv(gl.getUniformLocation(this.globeProgram, "u_projection"), false, projectionMatrix); - gl.uniformMatrix4fv(gl.getUniformLocation(this.globeProgram, "u_globeToMercMatrix"), false, globeToMercMatrix); - gl.uniform1f(gl.getUniformLocation(this.globeProgram, "u_globeToMercatorTransition"), transition); - gl.uniform2f(gl.getUniformLocation(this.globeProgram, "u_centerInMerc"), centerInMercator[0], centerInMercator[1]); - gl.uniform1f(gl.getUniformLocation(this.globeProgram, "u_pixelsPerMeterRatio"), pixelsPerMeterRatio); gl.drawArrays(gl.POINTS, 0, primitiveCount); - } else { // mercator projection + } else { + // mercator projection gl.useProgram(this.mercProgram); updateVboAndActivateAttrib(gl, this.mercProgram, this.posMercVbo, this.posMerc, "a_pos_merc"); gl.uniformMatrix4fv(gl.getUniformLocation(this.mercProgram, "u_projection"), false, projectionMatrix); diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index 81326e91c1d..34ef8572d9a 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -77,8 +77,18 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty context.setDepthMode(depthMode); if (painter.transform.projection.name === "globe") { - const center = painter.transform.pointMerc; - implementation.render(context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection(), painter.transform.globeToMercatorMatrix(), globeToMercatorTransition(painter.transform.zoom), [center.x, center.y], painter.transform.pixelsPerMeterRatio); + const shaderProgram = implementation.getShaderProgram(painter.transform.getProjection()); + if (shaderProgram) { + context.gl.useProgram(shaderProgram); + const center = painter.transform.pointMerc; + context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, painter.transform.customLayerMatrix()); + context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_globeToMercMatrix"), false, painter.transform.globeToMercatorMatrix()); + context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_globeToMercatorTransition"), globeToMercatorTransition(painter.transform.zoom)); + context.gl.uniform2f(context.gl.getUniformLocation(shaderProgram, "u_centerInMerc"), center.x, center.y); + context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_pixelsPerMeterRatio"), painter.transform.pixelsPerMeterRatio); + } + + implementation.render(context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection()); } else { implementation.render(context.gl, painter.transform.customLayerMatrix()); } diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index 7dc7f4d7e19..fd3a381ef17 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -155,6 +155,7 @@ export type CustomLayerInterface = { type: "custom", renderingMode: "2d" | "3d", render: CustomRenderMethod, + getShaderProgram: ?(projection: ?ProjectionSpecification) => ?WebGLProgram, prerender: ?CustomRenderMethod, renderToTile: ?(gl: WebGLRenderingContext, tileId: MercatorCoordinate) => void, shouldRerenderTiles: ?() => boolean, @@ -189,6 +190,33 @@ export function validateCustomStyleLayer(layerObject: CustomLayerInterface): Val return errors; } +export function globeCustomLayerVertexHeader(): string { + return ` + uniform mat4 u_projection; + uniform mat4 u_globeToMercMatrix; + uniform float u_globeToMercatorTransition; + uniform vec2 u_centerInMerc; + uniform float u_pixelsPerMeterRatio; + + vec4 project_custom_layer(vec3 pos_ecef, vec3 pos_merc) { + vec4 projected_pos = u_projection * u_globeToMercMatrix * vec4(pos_ecef, 1.); + projected_pos /= projected_pos.w; + + if (u_globeToMercatorTransition > 0.) { + vec4 merc = vec4(pos_merc, 1.); + merc.xy = (merc.xy - u_centerInMerc) * u_pixelsPerMeterRatio + u_centerInMerc; + merc.z *= u_pixelsPerMeterRatio; + + merc = u_projection * merc; + merc /= merc.w; + projected_pos = mix(projected_pos, merc, u_globeToMercatorTransition); + } + + return projected_pos; + } + `; +} + class CustomStyleLayer extends StyleLayer { implementation: CustomLayerInterface; diff --git a/src/ui/map.js b/src/ui/map.js index 8d642373f7b..b30b6a97aee 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -34,6 +34,7 @@ import SourceCache from '../source/source_cache.js'; import {GLOBE_ZOOM_THRESHOLD_MAX} from '../geo/projection/globe_util.js'; import {setCacheLimits} from '../util/tile_request_cache.js'; import {Debug} from '../util/debug.js'; +import {globeCustomLayerVertexHeader} from '../style/style_layer/custom_style_layer.js'; import type {PointLike} from '@mapbox/point-geometry'; import type {RequestTransformFunction} from '../util/mapbox.js'; @@ -3764,6 +3765,10 @@ class Map extends Camera { */ get version(): string { return version; } + + get globeCustomLayerVertexHeader(): string { + return globeCustomLayerVertexHeader(); + } } export default Map; From f086185f87f4e9bc4aee7faa9243753451bb35c5 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Fri, 27 Jan 2023 15:05:40 -0800 Subject: [PATCH 2/7] Simplify further --- debug/satellites-custom-layer.js | 41 +++++++++------------ src/render/draw_custom.js | 8 +++- src/style/style_layer/custom_style_layer.js | 10 +++-- src/ui/map.js | 6 +-- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/debug/satellites-custom-layer.js b/debug/satellites-custom-layer.js index ae2895c56b4..430a0bd10d4 100644 --- a/debug/satellites-custom-layer.js +++ b/debug/satellites-custom-layer.js @@ -7,18 +7,17 @@ const globeVertCode = ` void main() { gl_PointSize = 30.; - gl_Position = project_custom_layer(a_pos_ecef, a_pos_merc); + gl_Position = project_custom_layer(a_pos_merc, a_pos_ecef); } `; const mercVertCode = ` precision highp float; attribute vec3 a_pos_merc; - uniform mat4 u_projection; void main() { gl_PointSize = 30.; - gl_Position = u_projection * vec4(a_pos_merc, 1.); + gl_Position = project_custom_layer(a_pos_merc); } `; @@ -63,11 +62,13 @@ function createProgram(gl, vert, frag) { }; function updateVboAndActivateAttrib(gl, prog, vbo, data, attribName) { - gl.bindBuffer(gl.ARRAY_BUFFER, vbo); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW); const attribLoc = gl.getAttribLocation(prog, attribName); - gl.vertexAttribPointer(attribLoc, 3, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(attribLoc); + if (attribLoc !== -1) { + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(attribLoc, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(attribLoc); + } } const satellitesLayer = { @@ -82,8 +83,8 @@ const satellitesLayer = { this.posEcefVbo = gl.createBuffer(); this.posMercVbo = gl.createBuffer(); - this.globeProgram = createProgram(gl, map.globeCustomLayerVertexHeader.concat(globeVertCode), fragCode); - this.mercProgram = createProgram(gl, mercVertCode, fragCode); + this.globeProgram = createProgram(gl, map.customLayerVertexHeader.concat(globeVertCode), fragCode); + this.mercProgram = createProgram(gl, map.customLayerVertexHeader.concat(mercVertCode), fragCode); fetch('space-track-leo.txt').then(r => r.text()).then(rawData => { const tleData = rawData.replace(/\r/g, '') @@ -133,23 +134,15 @@ const satellitesLayer = { if (this.satData) { this.updateBuffers(); + const program = this.getShaderProgram(projection); const primitiveCount = this.posEcef.length / 3; gl.disable(gl.DEPTH_TEST); - if (projection && projection.name === 'globe') { - // globe projection and globe to mercator transition - gl.useProgram(this.globeProgram); - - updateVboAndActivateAttrib(gl, this.globeProgram, this.posEcefVbo, this.posEcef, "a_pos_ecef"); - updateVboAndActivateAttrib(gl, this.globeProgram, this.posMercVbo, this.posMerc, "a_pos_merc"); - - gl.drawArrays(gl.POINTS, 0, primitiveCount); - } else { - // mercator projection - gl.useProgram(this.mercProgram); - updateVboAndActivateAttrib(gl, this.mercProgram, this.posMercVbo, this.posMerc, "a_pos_merc"); - gl.uniformMatrix4fv(gl.getUniformLocation(this.mercProgram, "u_projection"), false, projectionMatrix); - gl.drawArrays(gl.POINTS, 0, primitiveCount); - } + gl.useProgram(program); + + updateVboAndActivateAttrib(gl, program, this.posEcefVbo, this.posEcef, "a_pos_ecef"); + updateVboAndActivateAttrib(gl, program, this.posMercVbo, this.posMerc, "a_pos_merc"); + + gl.drawArrays(gl.POINTS, 0, primitiveCount); } } }; \ No newline at end of file diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index 34ef8572d9a..bdf85a6891f 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -76,8 +76,8 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty context.setDepthMode(depthMode); + const shaderProgram = implementation.getShaderProgram && implementation.getShaderProgram(painter.transform.getProjection()); if (painter.transform.projection.name === "globe") { - const shaderProgram = implementation.getShaderProgram(painter.transform.getProjection()); if (shaderProgram) { context.gl.useProgram(shaderProgram); const center = painter.transform.pointMerc; @@ -90,7 +90,11 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty implementation.render(context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection()); } else { - implementation.render(context.gl, painter.transform.customLayerMatrix()); + if (shaderProgram) { + context.gl.useProgram(shaderProgram); + context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, painter.transform.customLayerMatrix()); + } + implementation.render(context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection()); } context.setDirty(); diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index fd3a381ef17..16b9f264506 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -7,7 +7,7 @@ import assert from 'assert'; import type {ValidationErrors} from '../validate_style.js'; import type {ProjectionSpecification} from '../../style-spec/types.js'; -type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array, projection: ?ProjectionSpecification, projectionToMercatorMatrix: ?Array, projectionToMercatorTransition: ?number, centerInMercator: ?Array, pixelsPerMeterRatio: ?number) => void; +type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array, projection: ?ProjectionSpecification) => void; /** * Interface for custom style layers. This is a specification for @@ -190,7 +190,7 @@ export function validateCustomStyleLayer(layerObject: CustomLayerInterface): Val return errors; } -export function globeCustomLayerVertexHeader(): string { +export function customLayerVertexHeader(): string { return ` uniform mat4 u_projection; uniform mat4 u_globeToMercMatrix; @@ -198,7 +198,7 @@ export function globeCustomLayerVertexHeader(): string { uniform vec2 u_centerInMerc; uniform float u_pixelsPerMeterRatio; - vec4 project_custom_layer(vec3 pos_ecef, vec3 pos_merc) { + vec4 project_custom_layer(vec3 pos_merc, vec3 pos_ecef) { vec4 projected_pos = u_projection * u_globeToMercMatrix * vec4(pos_ecef, 1.); projected_pos /= projected_pos.w; @@ -214,6 +214,10 @@ export function globeCustomLayerVertexHeader(): string { return projected_pos; } + + vec4 project_custom_layer(vec3 pos_merc) { + return u_projection * vec4(pos_merc, 1.); + } `; } diff --git a/src/ui/map.js b/src/ui/map.js index b30b6a97aee..cd2760f57bf 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -34,7 +34,7 @@ import SourceCache from '../source/source_cache.js'; import {GLOBE_ZOOM_THRESHOLD_MAX} from '../geo/projection/globe_util.js'; import {setCacheLimits} from '../util/tile_request_cache.js'; import {Debug} from '../util/debug.js'; -import {globeCustomLayerVertexHeader} from '../style/style_layer/custom_style_layer.js'; +import {customLayerVertexHeader} from '../style/style_layer/custom_style_layer.js'; import type {PointLike} from '@mapbox/point-geometry'; import type {RequestTransformFunction} from '../util/mapbox.js'; @@ -3766,8 +3766,8 @@ class Map extends Camera { get version(): string { return version; } - get globeCustomLayerVertexHeader(): string { - return globeCustomLayerVertexHeader(); + get customLayerVertexHeader(): string { + return customLayerVertexHeader(); } } From b34546a466656b66a90645da897c7d0e83fa9139 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Fri, 27 Jan 2023 16:43:07 -0800 Subject: [PATCH 3/7] Encode necessary matrix transformations on the CPU --- src/render/draw_custom.js | 26 ++++++++++++++++----- src/style/style_layer/custom_style_layer.js | 22 +++++++---------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index bdf85a6891f..75e10c56c68 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -6,7 +6,7 @@ import DepthMode from '../gl/depth_mode.js'; import StencilMode from '../gl/stencil_mode.js'; import {warnOnce} from '../util/util.js'; import {globeToMercatorTransition} from './../geo/projection/globe_util.js'; - +import {mat4} from 'gl-matrix'; import type Painter from './painter.js'; import type {OverscaledTileID} from '../source/tile_id.js'; import type SourceCache from '../source/source_cache.js'; @@ -14,6 +14,20 @@ import type CustomStyleLayer from '../style/style_layer/custom_style_layer.js'; import MercatorCoordinate from '../geo/mercator_coordinate.js'; import assert from 'assert'; +function createMercatorGlobeMatrix(projection, pixelsPerMeterRatio, centerInMerc) { + var mercToPixelMatrix = mat4.create(); + mat4.identity(mercToPixelMatrix); + + mercToPixelMatrix[0] = pixelsPerMeterRatio; + mercToPixelMatrix[5] = pixelsPerMeterRatio; + mercToPixelMatrix[10] = pixelsPerMeterRatio; + mercToPixelMatrix[12] = centerInMerc.x * (1.0 - pixelsPerMeterRatio); + mercToPixelMatrix[13] = centerInMerc.y * (1.0 - pixelsPerMeterRatio); + + return mat4.multiply([], projection, mercToPixelMatrix); +} + + function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomStyleLayer, coords: Array) { const context = painter.context; @@ -81,11 +95,11 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty if (shaderProgram) { context.gl.useProgram(shaderProgram); const center = painter.transform.pointMerc; - context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, painter.transform.customLayerMatrix()); - context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_globeToMercMatrix"), false, painter.transform.globeToMercatorMatrix()); - context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_globeToMercatorTransition"), globeToMercatorTransition(painter.transform.zoom)); - context.gl.uniform2f(context.gl.getUniformLocation(shaderProgram, "u_centerInMerc"), center.x, center.y); - context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_pixelsPerMeterRatio"), painter.transform.pixelsPerMeterRatio); + const globeProjection = mat4.multiply([], painter.transform.customLayerMatrix(), painter.transform.globeToMercatorMatrix()); + const mercatorProjection = createMercatorGlobeMatrix(painter.transform.customLayerMatrix(), painter.transform.pixelsPerMeterRatio, center) + context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, globeProjection); + context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_mercatorProjection"), false, mercatorProjection); + context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_transition"), globeToMercatorTransition(painter.transform.zoom)); } implementation.render(context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection()); diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index 16b9f264506..a02a0129193 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -193,30 +193,24 @@ export function validateCustomStyleLayer(layerObject: CustomLayerInterface): Val export function customLayerVertexHeader(): string { return ` uniform mat4 u_projection; - uniform mat4 u_globeToMercMatrix; - uniform float u_globeToMercatorTransition; - uniform vec2 u_centerInMerc; - uniform float u_pixelsPerMeterRatio; + uniform mat4 u_mercatorProjection; + uniform float u_transition; vec4 project_custom_layer(vec3 pos_merc, vec3 pos_ecef) { - vec4 projected_pos = u_projection * u_globeToMercMatrix * vec4(pos_ecef, 1.); + vec4 projected_pos = u_projection * vec4(pos_ecef, 1.0); projected_pos /= projected_pos.w; - if (u_globeToMercatorTransition > 0.) { - vec4 merc = vec4(pos_merc, 1.); - merc.xy = (merc.xy - u_centerInMerc) * u_pixelsPerMeterRatio + u_centerInMerc; - merc.z *= u_pixelsPerMeterRatio; - - merc = u_projection * merc; - merc /= merc.w; - projected_pos = mix(projected_pos, merc, u_globeToMercatorTransition); + if (u_transition > 0.0) { + vec4 mercator = u_mercatorProjection * vec4(pos_merc, 1.0); + mercator /= mercator.w; + projected_pos = mix(projected_pos, mercator, u_transition); } return projected_pos; } vec4 project_custom_layer(vec3 pos_merc) { - return u_projection * vec4(pos_merc, 1.); + return u_projection * vec4(pos_merc, 1.0); } `; } From 784e2b9b27e9a9644750450e69c9e9fd7c1acfd3 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Fri, 27 Jan 2023 17:07:41 -0800 Subject: [PATCH 4/7] Remove the need for split shader --- debug/satellites-custom-layer.js | 28 ++++++--------------- src/render/draw_custom.js | 24 ++++++++---------- src/style/style_layer/custom_style_layer.js | 27 ++++++++++---------- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/debug/satellites-custom-layer.js b/debug/satellites-custom-layer.js index 430a0bd10d4..3bf8f024e2e 100644 --- a/debug/satellites-custom-layer.js +++ b/debug/satellites-custom-layer.js @@ -1,7 +1,7 @@ const KM_TO_M = 1000; const TIME_STEP = 3 * 1000; -const globeVertCode = ` +const vertCode = ` attribute vec3 a_pos_ecef; attribute vec3 a_pos_merc; @@ -11,16 +11,6 @@ const globeVertCode = ` } `; -const mercVertCode = ` - precision highp float; - attribute vec3 a_pos_merc; - - void main() { - gl_PointSize = 30.; - gl_Position = project_custom_layer(a_pos_merc); - } -`; - const fragCode = ` precision highp float; uniform vec4 u_color; @@ -83,8 +73,7 @@ const satellitesLayer = { this.posEcefVbo = gl.createBuffer(); this.posMercVbo = gl.createBuffer(); - this.globeProgram = createProgram(gl, map.customLayerVertexHeader.concat(globeVertCode), fragCode); - this.mercProgram = createProgram(gl, map.customLayerVertexHeader.concat(mercVertCode), fragCode); + this.program = createProgram(gl, map.customLayerVertexHeader.concat(vertCode), fragCode); fetch('space-track-leo.txt').then(r => r.text()).then(rawData => { const tleData = rawData.replace(/\r/g, '') @@ -126,21 +115,20 @@ const satellitesLayer = { } }, - getShaderProgram (projection) { - return projection && projection.name === 'globe' ? this.globeProgram : this.mercProgram; + getShaderProgram () { + return this.program; }, - render (gl, projectionMatrix, projection) { + render (gl, projectionMatrix) { if (this.satData) { this.updateBuffers(); - const program = this.getShaderProgram(projection); const primitiveCount = this.posEcef.length / 3; gl.disable(gl.DEPTH_TEST); - gl.useProgram(program); + gl.useProgram(this.program); - updateVboAndActivateAttrib(gl, program, this.posEcefVbo, this.posEcef, "a_pos_ecef"); - updateVboAndActivateAttrib(gl, program, this.posMercVbo, this.posMerc, "a_pos_merc"); + updateVboAndActivateAttrib(gl, this.program, this.posEcefVbo, this.posEcef, "a_pos_ecef"); + updateVboAndActivateAttrib(gl, this.program, this.posMercVbo, this.posMerc, "a_pos_merc"); gl.drawArrays(gl.POINTS, 0, primitiveCount); } diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index 75e10c56c68..0bf7e543cb9 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -90,26 +90,24 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty context.setDepthMode(depthMode); - const shaderProgram = implementation.getShaderProgram && implementation.getShaderProgram(painter.transform.getProjection()); - if (painter.transform.projection.name === "globe") { - if (shaderProgram) { - context.gl.useProgram(shaderProgram); + const shaderProgram = implementation.getShaderProgram && implementation.getShaderProgram(); + if (shaderProgram) { + context.gl.useProgram(shaderProgram); + context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_isGlobe"), +(painter.transform.projection.name === "globe")); + context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_transition"), globeToMercatorTransition(painter.transform.zoom)); + + if (painter.transform.projection.name === "globe") { const center = painter.transform.pointMerc; const globeProjection = mat4.multiply([], painter.transform.customLayerMatrix(), painter.transform.globeToMercatorMatrix()); - const mercatorProjection = createMercatorGlobeMatrix(painter.transform.customLayerMatrix(), painter.transform.pixelsPerMeterRatio, center) + const mercatorProjection = createMercatorGlobeMatrix(painter.transform.customLayerMatrix(), painter.transform.pixelsPerMeterRatio, center); context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, globeProjection); context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_mercatorProjection"), false, mercatorProjection); - context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_transition"), globeToMercatorTransition(painter.transform.zoom)); - } - - implementation.render(context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection()); - } else { - if (shaderProgram) { - context.gl.useProgram(shaderProgram); + } else { context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, painter.transform.customLayerMatrix()); } - implementation.render(context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection()); } + + implementation.render(context.gl, painter.transform.customLayerMatrix()); context.setDirty(); painter.setBaseState(); diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index a02a0129193..1a83203eafa 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -7,7 +7,7 @@ import assert from 'assert'; import type {ValidationErrors} from '../validate_style.js'; import type {ProjectionSpecification} from '../../style-spec/types.js'; -type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array, projection: ?ProjectionSpecification) => void; +type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array) => void; /** * Interface for custom style layers. This is a specification for @@ -194,23 +194,24 @@ export function customLayerVertexHeader(): string { return ` uniform mat4 u_projection; uniform mat4 u_mercatorProjection; + uniform float u_isGlobe; uniform float u_transition; vec4 project_custom_layer(vec3 pos_merc, vec3 pos_ecef) { - vec4 projected_pos = u_projection * vec4(pos_ecef, 1.0); - projected_pos /= projected_pos.w; + if (u_isGlobe == 1.0) { + vec4 projected_pos = u_projection * vec4(pos_ecef, 1.0); + projected_pos /= projected_pos.w; - if (u_transition > 0.0) { - vec4 mercator = u_mercatorProjection * vec4(pos_merc, 1.0); - mercator /= mercator.w; - projected_pos = mix(projected_pos, mercator, u_transition); - } - - return projected_pos; - } + if (u_transition > 0.0) { + vec4 mercator = u_mercatorProjection * vec4(pos_merc, 1.0); + mercator /= mercator.w; + projected_pos = mix(projected_pos, mercator, u_transition); + } - vec4 project_custom_layer(vec3 pos_merc) { - return u_projection * vec4(pos_merc, 1.0); + return projected_pos; + } else { + return u_projection * vec4(pos_merc, 1.0); + } } `; } From 1b54482c5e3e0a923a622fd4e696337615265cd5 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Fri, 27 Jan 2023 17:11:30 -0800 Subject: [PATCH 5/7] Remove the need for split shader --- debug/satellites-custom-layer.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/debug/satellites-custom-layer.js b/debug/satellites-custom-layer.js index 3bf8f024e2e..9c5f20cff72 100644 --- a/debug/satellites-custom-layer.js +++ b/debug/satellites-custom-layer.js @@ -52,13 +52,11 @@ function createProgram(gl, vert, frag) { }; function updateVboAndActivateAttrib(gl, prog, vbo, data, attribName) { + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW); const attribLoc = gl.getAttribLocation(prog, attribName); - if (attribLoc !== -1) { - gl.bindBuffer(gl.ARRAY_BUFFER, vbo); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW); - gl.vertexAttribPointer(attribLoc, 3, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(attribLoc); - } + gl.vertexAttribPointer(attribLoc, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(attribLoc); } const satellitesLayer = { From ec03a25fa33162276f248e6ea3dbf782c8fa5254 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Mon, 30 Jan 2023 13:13:48 -0800 Subject: [PATCH 6/7] Fixups --- src/geo/transform.js | 14 ++++++-------- src/render/draw_custom.js | 12 +++--------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/geo/transform.js b/src/geo/transform.js index c7e96e6e710..5895c85ce58 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -1654,14 +1654,12 @@ class Transform { return this.mercatorMatrix.slice(); } - globeToMercatorMatrix(): ?Array { - if (this.projection.name === 'globe') { - const pixelsToMerc = 1 / this.worldSize; - const m = mat4.fromScaling([], [pixelsToMerc, pixelsToMerc, pixelsToMerc]); - mat4.multiply(m, m, this.globeMatrix); - return m; - } - return undefined; + globeToMercatorMatrix(): Array { + assert(this.projection.name === 'globe'); + const pixelsToMerc = 1 / this.worldSize; + const m = mat4.fromScaling([], [pixelsToMerc, pixelsToMerc, pixelsToMerc]); + mat4.multiply(m, m, this.globeMatrix); + return m; } recenterOnTerrain() { diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index 0bf7e543cb9..18841ca0489 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -15,7 +15,7 @@ import MercatorCoordinate from '../geo/mercator_coordinate.js'; import assert from 'assert'; function createMercatorGlobeMatrix(projection, pixelsPerMeterRatio, centerInMerc) { - var mercToPixelMatrix = mat4.create(); + const mercToPixelMatrix = mat4.create(); mat4.identity(mercToPixelMatrix); mercToPixelMatrix[0] = pixelsPerMeterRatio; @@ -27,7 +27,6 @@ function createMercatorGlobeMatrix(projection, pixelsPerMeterRatio, centerInMerc return mat4.multiply([], projection, mercToPixelMatrix); } - function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomStyleLayer, coords: Array) { const context = painter.context; @@ -46,12 +45,7 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty painter.setCustomLayerDefaults(); context.setColorMode(painter.colorModeForRenderPass()); - if (painter.transform.projection.name === "globe") { - const center = painter.transform.pointMerc; - prerender.call(implementation, context.gl, painter.transform.customLayerMatrix(), painter.transform.getProjection(), painter.transform.globeToMercatorMatrix(), globeToMercatorTransition(painter.transform.zoom), [center.x, center.y], painter.transform.pixelsPerMeterRatio); - } else { - prerender.call(implementation, context.gl, painter.transform.customLayerMatrix()); - } + prerender.call(implementation, context.gl, painter.transform.customLayerMatrix()); context.setDirty(); painter.setBaseState(); @@ -106,7 +100,7 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, painter.transform.customLayerMatrix()); } } - + implementation.render(context.gl, painter.transform.customLayerMatrix()); context.setDirty(); From 62667a820d117aa1632443736ab01d76e131d906 Mon Sep 17 00:00:00 2001 From: Karim Naaji Date: Mon, 30 Jan 2023 15:02:34 -0800 Subject: [PATCH 7/7] Cleanups --- src/render/draw_custom.js | 16 ++++++++-------- src/style/style_layer/custom_style_layer.js | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/render/draw_custom.js b/src/render/draw_custom.js index 18841ca0489..fc43c4f3c79 100644 --- a/src/render/draw_custom.js +++ b/src/render/draw_custom.js @@ -84,20 +84,20 @@ function drawCustom(painter: Painter, sourceCache: SourceCache, layer: CustomSty context.setDepthMode(depthMode); - const shaderProgram = implementation.getShaderProgram && implementation.getShaderProgram(); - if (shaderProgram) { - context.gl.useProgram(shaderProgram); - context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_isGlobe"), +(painter.transform.projection.name === "globe")); - context.gl.uniform1f(context.gl.getUniformLocation(shaderProgram, "u_transition"), globeToMercatorTransition(painter.transform.zoom)); + const program = implementation.getShaderProgram && implementation.getShaderProgram(); + if (program) { + context.gl.useProgram(program); + layer.setUniform(context.gl, program, "u_isGlobe", +(painter.transform.projection.name === "globe")); + layer.setUniform(context.gl, program, "u_transition", globeToMercatorTransition(painter.transform.zoom)); if (painter.transform.projection.name === "globe") { const center = painter.transform.pointMerc; const globeProjection = mat4.multiply([], painter.transform.customLayerMatrix(), painter.transform.globeToMercatorMatrix()); const mercatorProjection = createMercatorGlobeMatrix(painter.transform.customLayerMatrix(), painter.transform.pixelsPerMeterRatio, center); - context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, globeProjection); - context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_mercatorProjection"), false, mercatorProjection); + layer.setUniform(context.gl, program, "u_projection", globeProjection); + layer.setUniform(context.gl, program, "u_mercatorProjection", mercatorProjection); } else { - context.gl.uniformMatrix4fv(context.gl.getUniformLocation(shaderProgram, "u_projection"), false, painter.transform.customLayerMatrix()); + layer.setUniform(context.gl, program, "u_projection", painter.transform.customLayerMatrix()); } } diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index 1a83203eafa..17a127060f9 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -5,6 +5,7 @@ import MercatorCoordinate from '../../geo/mercator_coordinate.js'; import type Map from '../../ui/map.js'; import assert from 'assert'; import type {ValidationErrors} from '../validate_style.js'; +import {warnOnce} from '../../util/util.js'; import type {ProjectionSpecification} from '../../style-spec/types.js'; type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array) => void; @@ -220,9 +221,27 @@ class CustomStyleLayer extends StyleLayer { implementation: CustomLayerInterface; + uniformLocation: {string: WebGLUniformLocation}; + constructor(implementation: CustomLayerInterface) { super(implementation, {}); this.implementation = implementation; + this.uniformLocation = {}; + } + + setUniform(gl: WebGLRenderingContext, program: WebGLProgram, name: string, value: number | Array) { + this.uniformLocation[name] = this.uniformLocation[name] || gl.getUniformLocation(program, name); + if (this.uniformLocation[name]) { + if (Array.isArray(value)) { + gl.uniformMatrix4fv(this.uniformLocation[name], false, value); + } else if (typeof value === 'number') { + gl.uniform1f(this.uniformLocation[name], value); + } else { + assert(false, "Unimplemented custom uniform type"); + } + } else { + warnOnce(`Layer "${this.id}" is missing uniform location "${name}", make sure to include shader prelude with map.customLayerVertexHeader as part of your custom layer shader source`); + } } is3D(): boolean {