Skip to content

Commit

Permalink
[MAPS3D-425] introduce skirts for globe rendering
Browse files Browse the repository at this point in the history
Absent skirts lead to holes for hight exaggerated terrain in globe mode.
  • Loading branch information
alexey-romanov committed Jan 18, 2023
1 parent 184421e commit 7a6ac35
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 40 deletions.
2 changes: 1 addition & 1 deletion debug/3d-playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
enableTerrain.onFinishChange((value) => {
map.setTerrain(value ? {"source": "mapbox-dem", "exaggeration": demo3d.terrainExaggeration} : null);
});
var terrainExaggeration = terrain.add(demo3d, 'terrainExaggeration', 0, 2);
var terrainExaggeration = terrain.add(demo3d, 'terrainExaggeration', 0, 100);
terrainExaggeration.onFinishChange((value) => {
if (demo3d.enableTerrain) {
map.setTerrain({"source": "mapbox-dem", "exaggeration": value});
Expand Down
147 changes: 117 additions & 30 deletions src/geo/projection/globe_util.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ function mercatorTileCornersInCameraSpace({x, y, z}: CanonicalTileID, numTiles:

// // Ensure that the tile viewed is the nearest when across the antimeridian
let wrap = 0;
const tileCenterXFromCamera = (w + e) / 2 - camX;
const tileCenterXFromCamera = (w + e) / 2 - camX;
if (tileCenterXFromCamera > .5) {
wrap = -1;
} else if (tileCenterXFromCamera < -.5) {
Expand All @@ -389,10 +389,10 @@ function mercatorTileCornersInCameraSpace({x, y, z}: CanonicalTileID, numTiles:
camY *= numTiles;

// Transform Mercator coordinates to points on the plane tangent to the globe at cameraCenter.
w = ((w + wrap) * numTiles - camX) * mercatorScale + camX;
e = ((e + wrap) * numTiles - camX) * mercatorScale + camX;
n = (n * numTiles - camY) * mercatorScale + camY;
s = (s * numTiles - camY) * mercatorScale + camY;
w = ((w + wrap) * numTiles - camX) * mercatorScale + camX;
e = ((e + wrap) * numTiles - camX) * mercatorScale + camX;
n = (n * numTiles - camY) * mercatorScale + camY;
s = (s * numTiles - camY) * mercatorScale + camY;

return [[w, s, 0],
[e, s, 0],
Expand Down Expand Up @@ -691,6 +691,12 @@ const POLE_RAD = degToRad(85.0);
const POLE_COS = Math.cos(POLE_RAD);
const POLE_SIN = Math.sin(POLE_RAD);

type GridWithLods = {
vertices: PosArray,
indices: TriangleIndexArray,
segments: Array<SegmentVector>
};

export class GlobeSharedBuffers {
_poleNorthVertexBuffer: VertexBuffer;
_poleSouthVertexBuffer: VertexBuffer;
Expand All @@ -700,6 +706,7 @@ export class GlobeSharedBuffers {
_gridBuffer: VertexBuffer;
_gridIndexBuffer: IndexBuffer;
_gridSegments: Array<SegmentVector>;
_gridSegmentsSkirts: Array<SegmentVector>;

_wireframeIndexBuffer: IndexBuffer;
_wireframeSegments: Array<SegmentVector>;
Expand All @@ -724,37 +731,117 @@ export class GlobeSharedBuffers {
}
}

_createGrid(context: Context) {
const gridVertices = new PosArray();
const gridIndices = new TriangleIndexArray();

const quadExt = GLOBE_VERTEX_GRID_SIZE;
const vertexExt = quadExt + 1;

for (let j = 0; j < vertexExt; j++)
for (let i = 0; i < vertexExt; i++)
gridVertices.emplaceBack(i, j);

this._gridSegments = [];
for (let k = 0, primitiveOffset = 0; k < GLOBE_LATITUDINAL_GRID_LOD_TABLE.length; k++) {
const latitudinalLod = GLOBE_LATITUDINAL_GRID_LOD_TABLE[k];
for (let j = 0; j < latitudinalLod; j++) {
for (let i = 0; i < quadExt; i++) {
const index = j * vertexExt + i;
gridIndices.emplaceBack(index + 1, index, index + vertexExt);
gridIndices.emplaceBack(index + vertexExt, index + vertexExt + 1, index + 1);
// Generate terrain grid vertices and indices for all LOD's
//
// Grid vertices memory layout:
//
// First line Skirt
// ┌───────────────┐
// │┌─────────────┐│
// Left ││┼┼┼┼┼┼┼┼┼┼┼┼┼││ Right
// Border ││┼┼┼┼┼┼┼┼┼┼┼┼┼││ Border
// Skirt │├─────────────┤│ Skirt
// ││ Main Grid ││
// │├─────────────┤│
// ││┼┼┼┼┼┼┼┼┼┼┼┼┼││
// ││┼┼┼┼┼┼┼┼┼┼┼┼┼││
// │└─────────────┘│
// ├───────────────┤
// ├───────────────┤
// └───────────────┘
// Bottom Skirt = Number of LOD's
//
_fillGridMeshWithLods(longitudinalCellsCount: number, latitudinalLods: number[]): GridWithLods {
const embedSkirt = true; // Change for debugging purposes (for complete skirt removal)

const vertices = new PosArray();
const indices = new TriangleIndexArray();
const segments: Array<SegmentVector> = [];

const xVertices = longitudinalCellsCount + 1 + 2 * (embedSkirt ? 1 : 0);
const yVerticesHighLodNoStrip = latitudinalLods[0] + 1;
const yVerticesHighLodWithStrip = latitudinalLods[0] + 1 + (embedSkirt ? 1 + latitudinalLods.length : 0);

// Index adjustment, used to make strip (x, y) vertex input attribute data
// to match same data on ordinary grid edges
const prepareVertex = (x: number, y: number, isSkirt: boolean) => {
if (!embedSkirt) return [x, y];

let adjustedX = (() => {
if (x === xVertices - 1) {
return x - 2;
} else if (x === 0) {
return x;
} else {
return x - 1;
}
})();

// Skirt factor is introduces as an offset to the .x coordinate, similar to how it's done for mercator grids
const skirtOffset = 24575;
adjustedX += isSkirt ? skirtOffset : 0;

return [adjustedX, y];
};

// Add first horizontal strip if present
if (embedSkirt) {
for (let x = 0; x < xVertices; ++x) {
vertices.emplaceBack(...prepareVertex(x, 0, true));
}
}

const numVertices = (latitudinalLod + 1) * vertexExt;
const numPrimitives = latitudinalLod * quadExt * 2;
// Add main grid part with vertices strips embedded
for (let y = 0; y < yVerticesHighLodNoStrip; ++y) {
for (let x = 0; x < xVertices; ++x) {
const isSideBorder = (x === 0 || x === xVertices - 1);

this._gridSegments.push(SegmentVector.simpleSegment(0, primitiveOffset, numVertices, numPrimitives));
primitiveOffset += numPrimitives;
vertices.emplaceBack(...prepareVertex(x, y, isSideBorder && embedSkirt));
}
}

// Add bottom strips for each LOD
if (embedSkirt) {
for (let lodIdx = 0; lodIdx < latitudinalLods.length; ++lodIdx) {
const lastYRowForLod = latitudinalLods[lodIdx];
for (let x = 0; x < xVertices; ++x) {
vertices.emplaceBack(...prepareVertex(x, lastYRowForLod, true));
}
}
}

this._gridBuffer = context.createVertexBuffer(gridVertices, posAttributes.members);
this._gridIndexBuffer = context.createIndexBuffer(gridIndices, true);
// Fill triangles
for (let lodIdx = 0; lodIdx < latitudinalLods.length; ++lodIdx) {
const indexOffset = indices.length;

const yVerticesLod = latitudinalLods[lodIdx] + 1 + 2 * (embedSkirt ? 1 : 0);

for (let y = 0; y < yVerticesLod - 1; y++) {
const isLastLine = (y === yVerticesLod - 2);
const offsetToNextRow =
(isLastLine && embedSkirt ?
(xVertices * (yVerticesHighLodWithStrip - latitudinalLods.length + lodIdx - y)) :
xVertices);

for (let x = 0; x < xVertices - 1; x++) {
const idx = y * xVertices + x;

indices.emplaceBack(idx + 1, idx, idx + offsetToNextRow);
indices.emplaceBack(idx + offsetToNextRow, idx + offsetToNextRow + 1, idx + 1);
}
}
segments.push(SegmentVector.simpleSegment(0, indexOffset, vertices.length, indices.length - indexOffset));
}

return {vertices, indices, segments};
}

_createGrid(context: Context) {
const gridWithLods = this._fillGridMeshWithLods(GLOBE_VERTEX_GRID_SIZE, GLOBE_LATITUDINAL_GRID_LOD_TABLE);
this._gridSegments = gridWithLods.segments;

this._gridBuffer = context.createVertexBuffer(gridWithLods.vertices, posAttributes.members);
this._gridIndexBuffer = context.createIndexBuffer(gridWithLods.indices, true);
}

_createPoles(context: Context) {
Expand Down
12 changes: 12 additions & 0 deletions src/shaders/_prelude_terrain.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ vec3 elevationVector(vec2 pos) { return vec3(0, 0, 1); }

#endif

// Handle skirt flag for terrain & globe shaders

const float skirtOffset = 24575.0;
vec3 decomposeToPosAndSkirt(vec2 posWithComposedSkirt)
{
float skirt = float(posWithComposedSkirt.x >= skirtOffset);
vec2 pos = posWithComposedSkirt - vec2(skirt * skirtOffset, 0.0);

return vec3(pos, skirt);
}


#ifdef TERRAIN

#ifdef TERRAIN_DEM_FLOAT_FORMAT
Expand Down
10 changes: 7 additions & 3 deletions src/shaders/globe_raster.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ uniform mat4 u_merc_matrix;
uniform float u_zoom_transition;
uniform vec2 u_merc_center;
uniform mat3 u_grid_matrix;
uniform float u_skirt_height;

#ifdef GLOBE_POLES
attribute vec3 a_globe_pos;
attribute vec2 a_uv;
#else
attribute vec2 a_pos;
attribute vec2 a_pos; // .xy - grid coords, .z - 1 - skirt, 0 - grid
#endif

varying vec2 v_pos0;
Expand Down Expand Up @@ -52,7 +53,9 @@ void main() {
float idx = u_grid_matrix[1][2];
float idy = u_grid_matrix[2][2];

vec3 latLng = u_grid_matrix * vec3(a_pos, 1.0);
vec3 decomposedPosAndSkirt = decomposeToPosAndSkirt(a_pos);

vec3 latLng = u_grid_matrix * vec3(decomposedPosAndSkirt.xy, 1.0);

float mercatorY = mercatorYfromLat(latLng[0]);
float uvY = mercatorY * tiles - idy;
Expand All @@ -72,10 +75,11 @@ void main() {
// Normal vector can be derived from the ecef position
// as "elevationVector" can't be queried outside of the tile
vec3 up_vector = normalize(globe_pos) * u_tile_up_scale;
float height = elevation(tile_pos);
#else
vec3 up_vector = elevationVector(tile_pos);
float height = mix(elevation(tile_pos), -u_skirt_height, decomposedPosAndSkirt.z);
#endif
float height = elevation(tile_pos);

#ifdef TERRAIN_WIREFRAME
height += wireframeOffset;
Expand Down
6 changes: 3 additions & 3 deletions src/shaders/terrain_raster.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ varying vec4 v_pos_light_view_1;
varying float v_depth;
#endif

const float skirtOffset = 24575.0;
const float wireframeOffset = 0.00015;

void main() {
float skirt = float(a_pos.x >= skirtOffset);
vec2 decodedPos = a_pos - vec2(skirt * skirtOffset, 0.0);
vec3 decomposedPosAndSkirt = decomposeToPosAndSkirt(a_pos);
float skirt = decomposedPosAndSkirt.z;
vec2 decodedPos = decomposedPosAndSkirt.xy;
float elevation = elevation(decodedPos) - skirt * u_skirt_height;
#ifdef TERRAIN_WIREFRAME
elevation += wireframeOffset;
Expand Down
8 changes: 6 additions & 2 deletions src/terrain/draw_terrain_raster.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ function drawTerrainForGlobe(painter: Painter, terrain: Terrain, sourceCache: So
const elevationOptions = {useDenormalizedUpVectorScale: true};

batches.forEach(isWireframe => {
const tr = painter.transform;
// Use varying skirt hight applying limit for extremely large axaggerations
const skirtHeightValue = Math.min(skirtHeight(tr.zoom) * terrain.exaggeration(), 20000);

// This code assumes the rendering is batched into mesh terrain and then wireframe
// terrain (if applicable) so that this is enough to ensure the correct program is
// set when we switch from one to the other.
Expand Down Expand Up @@ -207,7 +211,7 @@ function drawTerrainForGlobe(painter: Painter, terrain: Terrain, sourceCache: So
const uniformValues = globeRasterUniformValues(
tr.projMatrix, globeMatrix, globeMercatorMatrix, normalizeMatrix, globeToMercatorTransition(tr.zoom),
mercatorCenter, tr.frustumCorners.TL, tr.frustumCorners.TR, tr.frustumCorners.BR,
tr.frustumCorners.BL, tr.globeCenterInViewSpace, tr.globeRadius, viewport, gridMatrix);
tr.frustumCorners.BL, tr.globeCenterInViewSpace, tr.globeRadius, viewport, skirtHeightValue, gridMatrix);

setShaderMode(shaderMode, isWireframe);

Expand Down Expand Up @@ -254,7 +258,7 @@ function drawTerrainForGlobe(painter: Painter, terrain: Terrain, sourceCache: So
context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled,
globeRasterUniformValues(tr.projMatrix, poleMatrix, poleMatrix, normalizeMatrix, 0.0, mercatorCenter,
tr.frustumCorners.TL, tr.frustumCorners.TR, tr.frustumCorners.BR, tr.frustumCorners.BL,
tr.globeCenterInViewSpace, tr.globeRadius, viewport), "globe_pole_raster", vertexBuffer,
tr.globeCenterInViewSpace, tr.globeRadius, viewport, 0), "globe_pole_raster", vertexBuffer,
indexBuffer, segment);

terrain.setupElevationDraw(tile, program, elevationOptions);
Expand Down
6 changes: 5 additions & 1 deletion src/terrain/globe_raster_program.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type GlobeRasterUniformsType = {|
'u_merc_center': Uniform2f,
'u_image0': Uniform1i,
'u_grid_matrix': UniformMatrix3f,
'u_skirt_height': Uniform1f,
'u_frustum_tl': Uniform3f,
'u_frustum_tr': Uniform3f,
'u_frustum_br': Uniform3f,
Expand Down Expand Up @@ -61,6 +62,7 @@ const globeRasterUniforms = (context: Context): GlobeRasterUniformsType => ({
'u_merc_center': new Uniform2f(context),
'u_image0': new Uniform1i(context),
'u_grid_matrix': new UniformMatrix3f(context),
'u_skirt_height': new Uniform1f(context),
'u_frustum_tl': new Uniform3f(context),
'u_frustum_tr': new Uniform3f(context),
'u_frustum_br': new Uniform3f(context),
Expand Down Expand Up @@ -103,6 +105,7 @@ const globeRasterUniformValues = (
globePosition: [number, number, number],
globeRadius: number,
viewport: [number, number],
skirtHeight: number,
gridMatrix: ?Mat4
): UniformValues<GlobeRasterUniformsType> => ({
'u_proj_matrix': Float32Array.from(projMatrix),
Expand All @@ -119,7 +122,8 @@ const globeRasterUniformValues = (
'u_globe_pos': globePosition,
'u_globe_radius': globeRadius,
'u_viewport': viewport,
'u_grid_matrix': gridMatrix ? Float32Array.from(gridMatrix) : new Float32Array(9)
'u_grid_matrix': gridMatrix ? Float32Array.from(gridMatrix) : new Float32Array(9),
'u_skirt_height': skirtHeight
});

const atmosphereUniformValues = (
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"version": 8,
"metadata": {
"test": {
"width": 1000,
"height": 512,
"allowed": 0.0004,
"operations": [
["setProjection", "globe"],
["wait", 500]
]
}
},
"zoom": 5.13,
"terrain": {
"source": "rgbterrain",
"exaggeration": 100.0
},
"pitch": 45,
"bearing": -48,
"center": [99.13, 47.46],
"sources": {
"satellite": {
"type": "raster",
"tiles": [
"local://tiles/{z}-{x}-{y}.satellite.png"
],
"tileSize": 256
},
"rgbterrain": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.512.png"
],
"maxzoom": 12,
"tileSize": 512
}
},
"fog": {
"star-intensity": 0
},
"layers": [
{
"id": "satellite",
"type": "raster",
"source": "satellite",
"paint": {
"raster-fade-duration": 0
}
}
]
}

0 comments on commit 7a6ac35

Please sign in to comment.