Skip to content

Commit b9ada56

Browse files
stephanheiglgithub-actions[bot]
authored andcommitted
Fix z-fighting for elevated raster layers.
Add paint property raster-elevation-reference to elevate raster layers relative to ground instead of sea level. GitOrigin-RevId: 8c8269d43c23ddc0647b26184123baa010556359
1 parent a721516 commit b9ada56

12 files changed

Lines changed: 243 additions & 37 deletions

File tree

src/geo/projection/globe_util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ export class GlobeSharedBuffers {
884884
const poleVertices = GLOBE_VERTEX_GRID_SIZE + 2;
885885
this._poleSegments = [];
886886

887-
for (let zoom = 0, offset = 0; zoom < GLOBE_ZOOM_THRESHOLD_MIN; zoom++) {
887+
for (let zoom = 0, offset = 0; zoom < GLOBE_ZOOM_THRESHOLD_MAX; zoom++) {
888888
const tiles = 1 << zoom;
889889
const endAngle = 360.0 / tiles;
890890

src/render/draw_raster.ts

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {mercatorXfromLng, mercatorYfromLat} from '../geo/mercator_coordinate';
2323
import {COLOR_MIX_FACTOR} from '../style/style_layer/raster_style_layer';
2424
import RasterArrayTile from '../source/raster_array_tile';
2525
import RasterArrayTileSource from '../source/raster_array_tile_source';
26+
import ColorMode from '../gl/color_mode';
2627

2728
import type Transform from '../geo/transform';
2829
import type {OverscaledTileID} from '../source/tile_id';
@@ -62,10 +63,16 @@ function adjustColorMix(colorMix: [number, number, number, number]): [number, nu
6263

6364
function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterStyleLayer, tileIDs: Array<OverscaledTileID>, variableOffsets?: Partial<Record<CrossTileID, VariableOffset>>, isInitialLoad?: boolean) {
6465
if (painter.renderPass !== 'translucent') return;
65-
if (layer.paint.get('raster-opacity') === 0) return;
66+
67+
const rasterOpacity = layer.paint.get('raster-opacity');
68+
if (rasterOpacity === 0) return;
69+
6670
const isGlobeProjection = painter.transform.projection.name === 'globe';
6771
const renderingWithElevation = layer.paint.get('raster-elevation') !== 0.0;
6872
const renderingElevatedOnGlobe = renderingWithElevation && isGlobeProjection;
73+
const isElevationReferenceTerrainGroundLevel = painter.terrain && painter.terrain.exaggeration() > 0 && renderingWithElevation && layer.paint.get('raster-elevation-reference') === 'ground';
74+
const renderingElevatedOnTerrain = !isGlobeProjection && isElevationReferenceTerrainGroundLevel;
75+
6976
if (painter.renderElevatedRasterBackface && !renderingElevatedOnGlobe) {
7077
return;
7178
}
@@ -117,7 +124,10 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
117124
rasterConfig.defines.push("PROJECTION_GLOBE_VIEW");
118125
}
119126
if (renderingWithElevation) {
120-
rasterConfig.defines.push("RENDER_CUTOFF");
127+
rasterConfig.defines.push("RENDER_CUTOFF", "ELEVATED");
128+
}
129+
if (isElevationReferenceTerrainGroundLevel) {
130+
rasterConfig.defines.push("ELEVATION_REFERENCE_GROUND");
121131
}
122132

123133
const drawTiles = (tiles: Array<OverscaledTileID>, cullFaceMode: CullFaceMode, elevatedStencilMode?: StencilMode) => {
@@ -131,7 +141,7 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
131141
if (!textureDescriptor || !textureDescriptor.texture) continue;
132142
const {texture, mix: rasterColorMix, offset: rasterColorOffset, tileSize, buffer} = textureDescriptor;
133143

134-
let depthMode;
144+
let depthMode: DepthMode;
135145
let projMatrix;
136146
if (renderingToTexture) {
137147
depthMode = DepthMode.disabled;
@@ -143,7 +153,7 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
143153
// Set the lower zoom level to sublayer 0, and higher zoom levels to higher sublayers
144154
// Use gl.LESS to prevent double drawing in areas where tiles overlap.
145155
depthMode = painter.depthModeForSublayer(coord.overscaledZ - minTileZ,
146-
layer.paint.get('raster-opacity') === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS);
156+
rasterOpacity === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS);
147157
projMatrix = painter.transform.calculateProjMatrix(unwrappedTileID, align);
148158
}
149159

@@ -229,6 +239,7 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
229239
}
230240

231241
const uniformValues = rasterUniformValues(
242+
painter,
232243
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
233244
projMatrix,
234245
normalizeMatrix,
@@ -263,7 +274,7 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
263274
const elevatedGlobeIndexBuffer = source.elevatedGlobeIndexBuffer;
264275
if (renderingToTexture || !isGlobeProjection) {
265276
if (source.boundsBuffer && source.boundsSegments) program.draw(
266-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
277+
267278
painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.disabled,
268279
uniformValues, layer.id, source.boundsBuffer,
269280
painter.quadTriangleIndexBuffer, source.boundsSegments);
@@ -273,7 +284,7 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
273284
source.getSegmentsForLongitude(tr.center.lng);
274285
if (segments) {
275286
program.draw(
276-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
287+
277288
painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, cullFaceMode,
278289
uniformValues, layer.id, elevatedGlobeVertexBuffer,
279290
elevatedGlobeIndexBuffer, segments);
@@ -283,17 +294,36 @@ function drawRaster(painter: Painter, sourceCache: SourceCache, layer: RasterSty
283294
depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadOnly, painter.depthRangeFor3D);
284295
const sharedBuffers = painter.globeSharedBuffers;
285296
if (sharedBuffers) {
297+
if (isElevationReferenceTerrainGroundLevel) {
298+
painter.terrain.setupElevationDraw(tile, program);
299+
painter.uploadCommonUniforms(context, program, tile.tileID.toUnwrapped());
300+
}
286301
const [buffer, indexBuffer, segments] = sharedBuffers.getGridBuffers(latitudinalLod, false);
287302
assert(buffer);
288303
assert(indexBuffer);
289304
assert(segments);
290-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
305+
291306
program.draw(painter, gl.TRIANGLES, depthMode, elevatedStencilMode || stencilMode, painter.colorModeForRenderPass(), cullFaceMode, uniformValues, layer.id, buffer, indexBuffer, segments);
292307
}
308+
} else if (renderingElevatedOnTerrain) {
309+
depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, painter.depthRangeFor3D);
310+
painter.terrain.setupElevationDraw(tile, program);
311+
painter.uploadCommonUniforms(context, program, tile.tileID.toUnwrapped());
312+
313+
// 2-pass rendering in case layer is not fully opaque and we are looking at higher pitch angles
314+
const pitchThresholdForTwoPassRendering = 20.0;
315+
if (tr.pitch > pitchThresholdForTwoPassRendering) {
316+
program.draw(painter, gl.TRIANGLES, depthMode, StencilMode.disabled, ColorMode.disabled, CullFaceMode.frontCCW,
317+
uniformValues, layer.id, painter.terrain.gridBuffer,
318+
painter.terrain.gridIndexBuffer, painter.terrain.gridSegments);
319+
}
320+
321+
program.draw(painter, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW,
322+
uniformValues, layer.id, painter.terrain.gridBuffer,
323+
painter.terrain.gridIndexBuffer, painter.terrain.gridSegments);
293324
} else {
294325
const {tileBoundsBuffer, tileBoundsIndexBuffer, tileBoundsSegments} = painter.getTileBoundsBuffers(tile);
295326

296-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
297327
program.draw(painter, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled,
298328
uniformValues, layer.id, tileBoundsBuffer,
299329
tileBoundsIndexBuffer, tileBoundsSegments);
@@ -395,7 +425,7 @@ function drawPole(isNorth: boolean, coord: OverscaledTileID | null | undefined,
395425
}
396426
const rasterColorMix = adjustColorMix(rasterConfig.mix);
397427

398-
const uniformValues = rasterPoleUniformValues(projMatrix, normalizeMatrix, globeMatrix as Float32Array, globeToMercatorTransition(painter.transform.zoom), fade, layer, [0, 0], elevation, RASTER_COLOR_TEXTURE_UNIT, rasterColorMix, rasterConfig.offset, rasterConfig.range, emissiveStrength);
428+
const uniformValues = rasterPoleUniformValues(painter, projMatrix, normalizeMatrix, globeMatrix as Float32Array, globeToMercatorTransition(painter.transform.zoom), fade, layer, [0, 0], elevation, RASTER_COLOR_TEXTURE_UNIT, rasterColorMix, rasterConfig.offset, rasterConfig.range, emissiveStrength);
399429
const program = painter.getOrCreateProgram('raster', {defines});
400430

401431
painter.uploadCommonUniforms(context, program, null);

src/render/program/raster_program.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,21 @@ import {
99
} from '../uniform_binding';
1010
import {computeRasterColorMix, computeRasterColorOffset} from '../raster';
1111
import {COLOR_RAMP_RES} from '../../style/style_layer/raster_style_layer';
12-
import {contrastFactor, saturationFactor} from '../../util/util';
12+
import {contrastFactor, saturationFactor, clamp} from '../../util/util';
1313

14+
import type Painter from '../painter';
1415
import type Context from '../../gl/context';
1516
import type {UniformValues} from '../uniform_binding';
1617
import type RasterStyleLayer from '../../style/style_layer/raster_style_layer';
1718

19+
const lerp = (a: number, b: number, t: number) => { return (1 - t) * a + t * b; };
20+
21+
function computeZBiasFactor(painter: Painter) {
22+
const terrainExaggeration = Math.min(painter.terrain ? painter.terrain.exaggeration() : 0.0, 2.0);
23+
const bias = painter.transform.pitch < 15.0 ? lerp(0.07, 0.7, clamp((14.0 - painter.transform.zoom) / (14.0 - 9.0), 0.0, 1.0)) : 0.07;
24+
return bias * terrainExaggeration;
25+
}
26+
1827
export type RasterUniformsType = {
1928
['u_matrix']: UniformMatrix4f;
2029
['u_normalize_matrix']: UniformMatrix4f;
@@ -43,9 +52,10 @@ export type RasterUniformsType = {
4352
['u_texture_offset']: Uniform2f;
4453
['u_texture_res']: Uniform2f;
4554
['u_emissive_strength']: Uniform1f;
55+
['u_zbias_factor']: Uniform1f;
4656
};
4757

48-
export type RasterDefinesType = 'RASTER_COLOR' | 'RENDER_CUTOFF' | 'RASTER_ARRAY' | 'RASTER_ARRAY_LINEAR';
58+
export type RasterDefinesType = 'RASTER_COLOR' | 'RENDER_CUTOFF' | 'RASTER_ARRAY' | 'RASTER_ARRAY_LINEAR' | 'ELEVATION_REFERENCE_GROUND';
4959

5060
const rasterUniforms = (context: Context): RasterUniformsType => ({
5161
'u_matrix': new UniformMatrix4f(context),
@@ -74,10 +84,12 @@ const rasterUniforms = (context: Context): RasterUniformsType => ({
7484
'u_color_ramp': new Uniform1i(context),
7585
'u_texture_offset': new Uniform2f(context),
7686
'u_texture_res': new Uniform2f(context),
77-
'u_emissive_strength': new Uniform1f(context)
87+
'u_emissive_strength': new Uniform1f(context),
88+
'u_zbias_factor': new Uniform1f(context)
7889
});
7990

8091
const rasterUniformValues = (
92+
painter: Painter,
8193
matrix: Float32Array,
8294
normalizeMatrix: Float32Array,
8395
globeMatrix: Float32Array,
@@ -136,10 +148,12 @@ const rasterUniformValues = (
136148
tileSize / (tileSize + 2 * buffer)
137149
],
138150
'u_texture_res': [tileSize + 2 * buffer, tileSize + 2 * buffer],
139-
'u_emissive_strength': emissiveStrength
151+
'u_emissive_strength': emissiveStrength,
152+
'u_zbias_factor': computeZBiasFactor(painter)
140153
});
141154

142155
const rasterPoleUniformValues = (
156+
painter: Painter,
143157
matrix: Float32Array,
144158
normalizeMatrix: Float32Array,
145159
globeMatrix: Float32Array,
@@ -157,6 +171,7 @@ const rasterPoleUniformValues = (
157171
colorRange: [number, number],
158172
emissiveStrength: number,
159173
): UniformValues<RasterUniformsType> => (rasterUniformValues(
174+
painter,
160175
matrix,
161176
normalizeMatrix,
162177
globeMatrix,

src/shaders/raster.vertex.glsl

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "_prelude_fog.vertex.glsl"
2+
#include "_prelude_terrain.vertex.glsl"
23

34
uniform mat4 u_matrix;
45
uniform mat4 u_normalize_matrix;
@@ -13,6 +14,10 @@ uniform float u_raster_elevation;
1314
uniform float u_zoom_transition;
1415
uniform vec2 u_merc_center;
1516

17+
#ifdef ELEVATED
18+
uniform lowp float u_zbias_factor;
19+
#endif
20+
1621
#define GLOBE_UPSCALE GLOBE_RADIUS / 6371008.8
1722

1823
#ifdef GLOBE_POLES
@@ -34,9 +39,14 @@ void main() {
3439
vec2 uv;
3540
#ifdef GLOBE_POLES
3641
vec3 globe_pos = a_globe_pos;
37-
globe_pos += normalize(globe_pos) * u_raster_elevation * GLOBE_UPSCALE;
38-
gl_Position = u_matrix * u_globe_matrix * vec4(globe_pos , 1.0);
3942
uv = a_uv;
43+
float ele = u_raster_elevation;
44+
#ifdef ELEVATION_REFERENCE_GROUND
45+
ele += elevation(uv * EXTENT);
46+
#endif
47+
globe_pos += normalize(globe_pos) * ele * GLOBE_UPSCALE;
48+
gl_Position = u_matrix * u_globe_matrix * vec4(globe_pos , 1.0);
49+
4050
#ifdef FOG
4151
v_fog_pos = fog_position((u_normalize_matrix * vec4(a_globe_pos, 1.0)).xyz);
4252
#endif // FOG
@@ -49,18 +59,31 @@ void main() {
4959
// so math for modifying either is consistent.
5060
uv = a_texture_pos / 8192.0;
5161
#ifdef PROJECTION_GLOBE_VIEW
62+
5263
vec3 decomposed_pos_and_skirt = decomposeToPosAndSkirt(a_pos);
5364
vec3 latLng = u_grid_matrix * vec3(decomposed_pos_and_skirt.xy, 1.0);
54-
vec3 globe_pos = latLngToECEF(latLng.xy);
55-
globe_pos += normalize(globe_pos) * u_raster_elevation * GLOBE_UPSCALE;
56-
vec4 globe_world_pos = u_globe_matrix * vec4(globe_pos, 1.0);
57-
vec4 merc_world_pos = vec4(0.0);
5865
float mercatorY = mercatorYfromLat(latLng[0]);
59-
float mercatorX = mercatorXfromLng(latLng[1]);
66+
float mercatorX = mercatorXfromLng(latLng[1]);
67+
68+
float tiles = u_grid_matrix[0][2];
69+
if (tiles > 0.0) {
70+
float idx = u_grid_matrix[1][2];
71+
float idy = u_grid_matrix[2][2];
72+
float uvY = mercatorY * tiles - idy;
73+
float uvX = mercatorX * tiles - idx;
74+
uv = vec2(uvX, uvY);
75+
}
76+
77+
float ele = u_raster_elevation;
78+
#ifdef ELEVATION_REFERENCE_GROUND
79+
ele += elevation(uv * EXTENT) - decomposed_pos_and_skirt.z;
80+
#endif
81+
82+
vec4 merc_world_pos = vec4(0.0);
6083
v_split_fade = 0.0;
6184
if (u_zoom_transition > 0.0) {
6285
vec2 merc_pos = vec2(mercatorX, mercatorY);
63-
merc_world_pos = vec4(merc_pos, u_raster_elevation, 1.0);
86+
merc_world_pos = vec4(merc_pos, ele, 1.0);
6487
merc_world_pos.xy -= u_merc_center;
6588
merc_world_pos.x = wrap(merc_world_pos.x, -0.5, 0.5);
6689
merc_world_pos = u_merc_matrix * merc_world_pos;
@@ -72,15 +95,10 @@ void main() {
7295
v_split_fade = clamp(1.0 - v_split_fade, 0.0, 1.0);
7396
v_split_fade = max(smoothstep(1.0 - range, 1.0, dist_from_poles), max(smoothstep(1.0 - range, 1.0, v_split_fade), smoothstep(1.0 - range, 1.0, 1.0 - v_split_fade)));
7497
}
75-
76-
float tiles = u_grid_matrix[0][2];
77-
if (tiles > 0.0) {
78-
float idx = u_grid_matrix[1][2];
79-
float idy = u_grid_matrix[2][2];
80-
float uvY = mercatorY * tiles - idy;
81-
float uvX = mercatorX * tiles - idx;
82-
uv = vec2(uvX, uvY);
83-
}
98+
99+
vec3 globe_pos = latLngToECEF(latLng.xy);
100+
globe_pos += normalize(globe_pos) * ele * GLOBE_UPSCALE;
101+
vec4 globe_world_pos = u_globe_matrix * vec4(globe_pos, 1.0);
84102

85103
vec4 interpolated_pos = vec4(mix(globe_world_pos.xyz, merc_world_pos.xyz, u_zoom_transition) * w, w);
86104

@@ -89,9 +107,18 @@ void main() {
89107
v_fog_pos = fog_position((u_normalize_matrix * vec4(globe_pos, 1.0)).xyz);
90108
#endif // FOG
91109
#else // else PROJECTION_GLOBE_VIEW
92-
gl_Position = u_matrix * vec4(a_pos * w, u_raster_elevation * w, w);
110+
float ele = 0.0;
111+
vec2 decodedPos = a_pos;
112+
#ifdef ELEVATION_REFERENCE_GROUND
113+
vec3 decomposedPosAndSkirt = decomposeToPosAndSkirt(a_pos);
114+
float skirt = decomposedPosAndSkirt.z;
115+
decodedPos = decomposedPosAndSkirt.xy;
116+
uv = decodedPos / 8192.0;
117+
ele = elevation(decodedPos) - skirt;
118+
#endif // ELEVATION_REFERENCE_GROUND
119+
gl_Position = u_matrix * vec4(decodedPos * w, (u_raster_elevation + ele) * w, w);
93120
#ifdef FOG
94-
v_fog_pos = fog_position(a_pos);
121+
v_fog_pos = fog_position(decodedPos);
95122
#endif // FOG
96123
#endif // else PROJECTION_GLOBE_VIEW
97124
#endif // else GLOBE_POLES
@@ -104,6 +131,12 @@ void main() {
104131
v_pos0 = u_texture_offset.x + u_texture_offset.y * v_pos0;
105132
v_pos1 = u_texture_offset.x + u_texture_offset.y * v_pos1;
106133

134+
#ifdef ELEVATED
135+
float z = clamp(gl_Position.z / gl_Position.w, 0.5, 1.0);
136+
float zbias = max(0.00005, (pow(z, 0.8) - z) * u_zbias_factor);
137+
gl_Position.z -= (gl_Position.w * zbias);
138+
#endif
139+
107140
#ifdef RENDER_CUTOFF
108141
v_depth = gl_Position.z;
109142
#endif

src/source/source_cache.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class SourceCache extends Evented {
5252
_maxTileCacheSize?: number;
5353
_paused: boolean;
5454
_isRaster: boolean;
55+
_isRasterElevatedOverTerrain: boolean;
5556
_shouldReloadOnResume: boolean;
5657
_coveredTiles: Partial<Record<number | string, boolean>>;
5758
transform: Transform;
@@ -114,6 +115,7 @@ class SourceCache extends Evented {
114115
this._source.type === 'raster-dem' || this._source.type === 'raster-array' ||
115116
// @ts-expect-error - TS2339 - Property '_dataType' does not exist on type 'VideoSource | ImageSource | CanvasSource | CustomSource<ImageBitmap | HTMLCanvasElement | HTMLImageElement | ImageData>'.
116117
(this._source.type === 'custom' && this._source._dataType === 'raster');
118+
this._isRasterElevatedOverTerrain = false;
117119
}
118120

119121
onAdd(map: MapboxMap) {

0 commit comments

Comments
 (0)