Skip to content

Commit 38255cb

Browse files
authored
curve the sky over the globe (#8299)
1 parent c36bd57 commit 38255cb

10 files changed

Lines changed: 112 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- _...Add new stuff here..._
44

55
### 🐞 Bug fixes
6+
- Fix a gap between the sky and the ground at high pitch while globe transitions to mercator ([#7382](https://github.com/maplibre/maplibre-gl-js/issues/7382)) (by [@birkskyum](https://github.com/birkskyum))
67
- Treat an empty tile response (e.g. HTTP 204) as no data: raster-DEM tiles now load without elevation instead of failing with a `dem dimension mismatch` error, and empty raster tiles render as transparent ([#1551](https://github.com/maplibre/maplibre-gl-js/issues/1551)) (by [@clement-igonet](https://github.com/clement-igonet))
78
- _...Add new stuff here..._
89

src/geo/projection/globe_utils.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {describe, expect, test} from 'vitest';
22
import {LngLat} from '../lng_lat.ts';
3-
import {getGlobeCircumferencePixels, getZoomAdjustment, globeDistanceOfLocationsPixels} from './globe_utils.ts';
3+
import {getGlobeCenterInViewSpace, getGlobeCircumferencePixels, getGlobeRadiusPixels, getZoomAdjustment, globeDistanceOfLocationsPixels} from './globe_utils.ts';
4+
import {GlobeTransform} from './globe_transform.ts';
45

56
describe('globe utils', () => {
67
const digitsPrecision = 10;
@@ -47,4 +48,23 @@ describe('globe utils', () => {
4748
expect(getZoomAdjustment(0, 60)).toBeCloseTo(-1, digitsPrecision);
4849
expect(getZoomAdjustment(60, 0)).toBeCloseTo(1, digitsPrecision);
4950
});
51+
52+
test('getGlobeCenterInViewSpace', () => {
53+
const transform = new GlobeTransform();
54+
transform.resize(256, 512);
55+
transform.setMaxPitch(85);
56+
transform.setCenter(new LngLat(11.64, 47.55));
57+
transform.setZoom(11);
58+
const radius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
59+
60+
transform.setPitch(0);
61+
const straightDown = getGlobeCenterInViewSpace(transform);
62+
expect(straightDown[0]).toBeCloseTo(0, 6);
63+
expect(straightDown[1]).toBeCloseTo(0, 6);
64+
expect(straightDown[2]).toBeCloseTo(-(transform.cameraToCenterDistance + radius), 2);
65+
66+
transform.setPitch(85);
67+
const pitched = getGlobeCenterInViewSpace(transform);
68+
expect(Math.hypot(...pitched) / radius).toBeCloseTo(Math.hypot(...transform.cameraPosition), 8);
69+
});
5070
});

src/geo/projection/globe_utils.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {quat, type ReadonlyVec4, vec3} from 'gl-matrix';
1+
import {type mat4, quat, type ReadonlyVec4, vec3, vec4} from 'gl-matrix';
22
import {clamp, createVec3f64, createVec4f64, lerp, MAX_VALID_LATITUDE, mod, remapSaturate, scaleZoom, wrap} from '../../util/util.ts';
33
import {LngLat} from '../lng_lat.ts';
44
import {EXTENT} from '../../data/extent.ts';
@@ -88,6 +88,15 @@ export function getGlobeRadiusPixels(worldSize: number, latitudeDegrees: number)
8888
return worldSize / (2.0 * Math.PI) / Math.cos(latitudeDegrees * Math.PI / 180);
8989
}
9090

91+
/**
92+
* Returns the globe center in view space, in pixels: the camera is at the origin and looks down the negative Z axis.
93+
*/
94+
export function getGlobeCenterInViewSpace(transform: {modelViewProjectionMatrix: mat4; inverseProjectionMatrix: mat4}): vec3 {
95+
const position = vec4.transformMat4(createVec4f64(), [0, 0, 0, 1], transform.modelViewProjectionMatrix);
96+
vec4.transformMat4(position, position, transform.inverseProjectionMatrix);
97+
return [position[0] / position[3], position[1] / position[3], position[2] / position[3]];
98+
}
99+
91100
/**
92101
* Given a 3D point on the surface of a unit sphere, returns its angular coordinates in degrees.
93102
* The input vector must be normalized.

src/shaders/glsl/sky.fragment.glsl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,23 @@ uniform vec2 u_horizon;
55
uniform vec2 u_horizon_normal;
66
uniform float u_sky_horizon_blend;
77
uniform float u_sky_blend;
8+
uniform vec3 u_globe_position;
9+
uniform float u_globe_radius;
10+
uniform float u_camera_to_center_distance;
11+
12+
in vec3 v_view_direction;
813

914
void main() {
1015
float x = gl_FragCoord.x;
1116
float y = gl_FragCoord.y;
1217
float blend = (y - u_horizon.y) * u_horizon_normal.y + (x - u_horizon.x) * u_horizon_normal.x;
18+
if (u_sky_blend > 0.0) {
19+
vec3 ray = normalize(v_view_direction);
20+
float globe_distance = length(u_globe_position);
21+
float angle_to_globe_center = acos(clamp(dot(ray, u_globe_position) / globe_distance, -1.0, 1.0));
22+
float horizon_angle = asin(min(u_globe_radius / globe_distance, 1.0));
23+
blend = mix(blend, (angle_to_globe_center - horizon_angle) * u_camera_to_center_distance, u_sky_blend);
24+
}
1325
if (blend > 0.0) {
1426
if (blend < u_sky_horizon_blend) {
1527
fragColor = mix(u_sky_color, u_horizon_color, pow(1.0 - blend / u_sky_horizon_blend, 2.0));

src/shaders/glsl/sky.vertex.glsl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
layout(location = 0) in vec2 a_pos;
22

3+
uniform mat4 u_inv_proj_matrix;
4+
5+
out vec3 v_view_direction;
6+
37
void main() {
8+
v_view_direction = (u_inv_proj_matrix * vec4(a_pos, 0.0, 1.0)).xyz;
49
gl_Position = vec4(a_pos, 1.0, 1.0);
510
}

src/webgl/draw/draw_sky.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import {PosArray, TriangleIndexArray} from '../../data/array_types.g.ts';
66
import {SegmentVector} from '../../data/segment.ts';
77
import {skyUniformValues} from '../program/sky_program.ts';
88
import {atmosphereUniformValues} from '../program/atmosphere_program.ts';
9-
import {getGlobeRadiusPixels} from '../../geo/projection/globe_utils.ts';
9+
import {getGlobeCenterInViewSpace, getGlobeRadiusPixels} from '../../geo/projection/globe_utils.ts';
1010
import {Mesh} from '../../render/mesh.ts';
11-
import {mat4, vec3, vec4} from 'gl-matrix';
11+
import {mat4, vec3} from 'gl-matrix';
1212
import {ColorMode} from '../color_mode.ts';
1313

1414
import type {Sky} from '../../style/sky.ts';
@@ -95,22 +95,9 @@ export function drawAtmosphere(painter: Painter, sky: Sky, light: Light): void {
9595
}
9696

9797
const globeRadius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
98-
const invProjMatrix = transform.inverseProjectionMatrix;
99-
const vec = new Float64Array(4) as any as vec4;
100-
vec[3] = 1;
101-
vec4.transformMat4(vec, vec, transform.modelViewProjectionMatrix);
102-
vec[0] /= vec[3];
103-
vec[1] /= vec[3];
104-
vec[2] /= vec[3];
105-
vec[3] = 1;
106-
vec4.transformMat4(vec, vec, invProjMatrix);
107-
vec[0] /= vec[3];
108-
vec[1] /= vec[3];
109-
vec[2] /= vec[3];
110-
vec[3] = 1;
111-
const globePosition = [vec[0], vec[1], vec[2]] as vec3;
112-
113-
const uniformValues = atmosphereUniformValues(sunPos, atmosphereBlend, globePosition, globeRadius, invProjMatrix);
98+
const globePosition = getGlobeCenterInViewSpace(transform);
99+
100+
const uniformValues = atmosphereUniformValues(sunPos, atmosphereBlend, globePosition, globeRadius, transform.inverseProjectionMatrix);
114101

115102
const mesh = getMesh(context, sky);
116103

src/webgl/program/sky_program.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import {UniformColor, Uniform1f, Uniform2f} from '../uniform_binding.ts';
1+
import {UniformColor, Uniform1f, Uniform2f, Uniform3f, UniformMatrix4f} from '../uniform_binding.ts';
22
import type {Context} from '../../webgl/context.ts';
33
import type {UniformValues, UniformLocations} from '../uniform_binding.ts';
44
import {type IReadonlyTransform} from '../../geo/transform_interface.ts';
55
import {type Sky} from '../../style/sky.ts';
66
import {getMercatorHorizon} from '../../geo/projection/mercator_utils.ts';
7+
import {getGlobeCenterInViewSpace, getGlobeRadiusPixels} from '../../geo/projection/globe_utils.ts';
78

89
export type SkyUniformsType = {
910
'u_sky_color': UniformColor;
@@ -12,6 +13,10 @@ export type SkyUniformsType = {
1213
'u_horizon_normal': Uniform2f;
1314
'u_sky_horizon_blend': Uniform1f;
1415
'u_sky_blend': Uniform1f;
16+
'u_inv_proj_matrix': UniformMatrix4f;
17+
'u_globe_position': Uniform3f;
18+
'u_globe_radius': Uniform1f;
19+
'u_camera_to_center_distance': Uniform1f;
1520
};
1621

1722
const skyUniforms = (context: Context, locations: UniformLocations): SkyUniformsType => ({
@@ -21,6 +26,10 @@ const skyUniforms = (context: Context, locations: UniformLocations): SkyUniforms
2126
'u_horizon_normal': new Uniform2f(context, locations.u_horizon_normal),
2227
'u_sky_horizon_blend': new Uniform1f(context, locations.u_sky_horizon_blend),
2328
'u_sky_blend': new Uniform1f(context, locations.u_sky_blend),
29+
'u_inv_proj_matrix': new UniformMatrix4f(context, locations.u_inv_proj_matrix),
30+
'u_globe_position': new Uniform3f(context, locations.u_globe_position),
31+
'u_globe_radius': new Uniform1f(context, locations.u_globe_radius),
32+
'u_camera_to_center_distance': new Uniform1f(context, locations.u_camera_to_center_distance),
2433
});
2534

2635
const skyUniformValues = (sky: Sky, transform: IReadonlyTransform, pixelRatio: number): UniformValues<SkyUniformsType> => {
@@ -37,6 +46,10 @@ const skyUniformValues = (sky: Sky, transform: IReadonlyTransform, pixelRatio: n
3746
'u_horizon_normal': [-sinRoll, cosRoll],
3847
'u_sky_horizon_blend': (sky.properties.get('sky-horizon-blend') * transform.height / 2) * pixelRatio,
3948
'u_sky_blend': skyBlend,
49+
'u_inv_proj_matrix': transform.inverseProjectionMatrix,
50+
'u_globe_position': getGlobeCenterInViewSpace(transform),
51+
'u_globe_radius': getGlobeRadiusPixels(transform.worldSize, transform.center.lat),
52+
'u_camera_to_center_distance': transform.cameraToCenterDistance * pixelRatio,
4053
};
4154
};
4255

test/build/bundle_size.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"dist/maplibre-gl.mjs": {
3-
"raw": 578009,
4-
"gzip": 146197
3+
"raw": 579535,
4+
"gzip": 146573
55
},
66
"dist/maplibre-gl-worker.mjs": {
7-
"raw": 19131,
8-
"gzip": 6068
7+
"raw": 19181,
8+
"gzip": 6090
99
},
1010
"dist/maplibre-gl-shared.mjs": {
11-
"raw": 491922,
12-
"gzip": 138320
11+
"raw": 492100,
12+
"gzip": 138394
1313
}
1414
}
3.79 KB
Loading
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"version": 8,
3+
"metadata": {
4+
"test": {
5+
"height": 256,
6+
"width": 512,
7+
"maxPitch": 85,
8+
"operations": [
9+
[
10+
"wait"
11+
]
12+
]
13+
}
14+
},
15+
"center": [
16+
11.64,
17+
47.55
18+
],
19+
"zoom": 4.5,
20+
"pitch": 85,
21+
"sources": {},
22+
"layers": [],
23+
"sky": {
24+
"sky-color": "#199EF3",
25+
"horizon-color": "#daeff0",
26+
"sky-horizon-blend": 0.5,
27+
"atmosphere-blend": 0
28+
},
29+
"projection": {
30+
"type": [
31+
"interpolate",
32+
["linear"],
33+
["zoom"],
34+
4, "vertical-perspective",
35+
5, "mercator"
36+
]
37+
}
38+
}

0 commit comments

Comments
 (0)