Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sky Implementation according to spec #3645

Merged
merged 45 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ae58a36
copy code from old sky branch, but this branch is not ready yet
Jan 19, 2024
5af7c1c
add missing _fogMatrixCache clearance
Jan 30, 2024
1e7f6b1
change long comment from // to /** */ syntax
Jan 31, 2024
7918fba
serialize sky in map.getStyle()
Jan 31, 2024
32ae12b
add sky/fog expect tests (#3649)
acalcutt Feb 1, 2024
84de1de
fix sky2 calculateFogMatrix error and build error (#3651)
acalcutt Feb 2, 2024
7e07a31
Merge branch 'main' into sky2
HarelM Feb 2, 2024
f93c39e
Update test/build/min.test.ts
HarelM Feb 2, 2024
e359524
Merge branch 'main' into sky2
HarelM Feb 14, 2024
edbaa5d
Merge branch 'main' into sky2
HarelM Feb 29, 2024
797f156
Merge branch 'main' into sky2
HarelM Feb 29, 2024
865dbe9
Update diff function to take into consideration sky, updated tests
HarelM Feb 29, 2024
a6857a6
Move gl logic to draw_sky file, added tests to cover serialization.
HarelM Feb 29, 2024
6c084fc
Update docs comment
HarelM Feb 29, 2024
e9b16bb
Merge branch 'main' into sky2
HarelM Feb 29, 2024
6fed1af
Add missing tests to map.test.ts
HarelM Feb 29, 2024
3726ee9
Merge branch 'main' into sky2
HarelM Feb 29, 2024
757bcb5
Remove unneeded member variable
HarelM Mar 7, 2024
363529f
Fix build
HarelM Mar 7, 2024
8c23b36
Add cache to draw sky mesh buffer
HarelM Mar 7, 2024
b5b8793
Use globe's mesh idea, store mesh in sky object, similar to how it is…
HarelM Mar 7, 2024
61ad9c6
Merge branch 'main' into sky2
HarelM Jun 20, 2024
25e7d56
Fix tests
HarelM Jun 20, 2024
fa38f81
Fix names according to new spec
HarelM Jun 20, 2024
b44583d
Fix render and build tests
HarelM Jun 20, 2024
0fb7c88
Changed name to sky-horizon-blend
HarelM Jun 20, 2024
c1fb5c7
Fix lint
HarelM Jun 20, 2024
fe973a5
Add support for blend in fog and horizon
HarelM Jun 21, 2024
e9ec6b9
Terrain fog blending improvements (#4314)
kubapelc Jun 22, 2024
512d73e
Merge branch 'main' into sky2
HarelM Jun 22, 2024
8f1c5aa
Added a debug page, fixed a bug
HarelM Jun 22, 2024
d4fbe6d
Update the example to show the values in the spec.
HarelM Jun 22, 2024
964d177
Added test to reproduce the issue
HarelM Jun 22, 2024
f5bf079
Move sky test to a different folder
HarelM Jun 22, 2024
af0d86d
Fix lint
HarelM Jun 22, 2024
0e20a96
Remove unneeded reference from example
HarelM Jun 22, 2024
eda9122
Always initialize sky, remove ifs, fix tests
HarelM Jun 25, 2024
1d894c7
Update CHANGELOG.md
HarelM Jun 25, 2024
cd78810
Expect build to fail due to missing image in examples
HarelM Jun 26, 2024
6ae936d
Merge branch 'sky2' of https://github.com/maplibre/maplibre-gl-js int…
HarelM Jun 26, 2024
34ab2fc
Fix docs build
HarelM Jun 26, 2024
9ed0f6c
Fix docs build
HarelM Jun 26, 2024
25aa877
Compress image
HarelM Jun 26, 2024
d17bbe1
Update readme
HarelM Jun 26, 2024
592ded2
Merge branch 'main' into sky2
HarelM Jun 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 53 additions & 10 deletions src/geo/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ export class Transform {
rotationMatrix: mat2;
pixelsToGLUnits: [number, number];
cameraToCenterDistance: number;
cameraToSeaLevelDistance: number;
mercatorMatrix: mat4;
projMatrix: mat4;
invProjMatrix: mat4;
alignedProjMatrix: mat4;
fogMatrix: mat4;
pixelMatrix: mat4;
pixelMatrix3D: mat4;
pixelMatrixInverse: mat4;
Expand All @@ -57,6 +59,7 @@ export class Transform {
_constraining: boolean;
_posMatrixCache: {[_: string]: mat4};
_alignedPosMatrixCache: {[_: string]: mat4};
_fogMatrixCache: {[_: string]: mat4};

constructor(minZoom?: number, maxZoom?: number, minPitch?: number, maxPitch?: number, renderWorldCopies?: boolean) {
this.tileSize = 512; // constant
Expand All @@ -83,6 +86,7 @@ export class Transform {
this._edgeInsets = new EdgeInsets();
this._posMatrixCache = {};
this._alignedPosMatrixCache = {};
this._fogMatrixCache = {};
this.minElevationForCurrentTile = 0;
}

Expand Down Expand Up @@ -683,6 +687,17 @@ export class Transform {
}
}

calculateTileMatrix(unwrappedTileID: UnwrappedTileID): mat4 {
const canonical = unwrappedTileID.canonical;
const scale = this.worldSize / this.zoomScale(canonical.z);
const unwrappedX = canonical.x + Math.pow(2, canonical.z) * unwrappedTileID.wrap;

const worldMatrix = mat4.identity(new Float64Array(16) as any);
mat4.translate(worldMatrix, worldMatrix, [unwrappedX * scale, canonical.y * scale, 0]);
mat4.scale(worldMatrix, worldMatrix, [scale / EXTENT, scale / EXTENT, 1]);
return worldMatrix;
}

/**
* Calculate the posMatrix that, given a tile coordinate, would be used to display the tile on a map.
* @param unwrappedTileID - the tile ID
Expand All @@ -694,19 +709,32 @@ export class Transform {
return cache[posMatrixKey];
}

const canonical = unwrappedTileID.canonical;
const scale = this.worldSize / this.zoomScale(canonical.z);
const unwrappedX = canonical.x + Math.pow(2, canonical.z) * unwrappedTileID.wrap;

const posMatrix = mat4.identity(new Float64Array(16) as any);
mat4.translate(posMatrix, posMatrix, [unwrappedX * scale, canonical.y * scale, 0]);
mat4.scale(posMatrix, posMatrix, [scale / EXTENT, scale / EXTENT, 1]);
const posMatrix = this.calculateTileMatrix(unwrappedTileID);
mat4.multiply(posMatrix, aligned ? this.alignedProjMatrix : this.projMatrix, posMatrix);

cache[posMatrixKey] = new Float32Array(posMatrix);
return cache[posMatrixKey];
}

/**
* Calculate the fogMatrix that, given a tile coordinate, would be used to calculate fog on the map.
* @param unwrappedTileID - the tile ID
* @private
*/
calculateFogMatrix(unwrappedTileID: UnwrappedTileID): mat4 {
const posMatrixKey = unwrappedTileID.key;
const cache = this._fogMatrixCache;
if (cache[posMatrixKey]) {
return cache[posMatrixKey];
}

const fogMatrix = this.calculateTileMatrix(unwrappedTileID);
mat4.multiply(fogMatrix, this.fogMatrix, fogMatrix);

cache[posMatrixKey] = new Float32Array(fogMatrix);
return cache[posMatrixKey];
}

customLayerMatrix(): mat4 {
return this.mercatorMatrix.slice() as any;
}
Expand Down Expand Up @@ -814,11 +842,11 @@ export class Transform {
this.glCoordMatrix = m;

// Calculate the camera to sea-level distance in pixel in respect of terrain
const cameraToSeaLevelDistance = this.cameraToCenterDistance + this._elevation * this._pixelPerMeter / Math.cos(this._pitch);
this.cameraToSeaLevelDistance = this.cameraToCenterDistance + this._elevation * this._pixelPerMeter / Math.cos(this._pitch);
// In case of negative minimum elevation (e.g. the dead see, under the sea maps) use a lower plane for calculation
const minElevation = Math.min(this.elevation, this.minElevationForCurrentTile);
const cameraToLowestPointDistance = cameraToSeaLevelDistance - minElevation * this._pixelPerMeter / Math.cos(this._pitch);
const lowestPlane = minElevation < 0 ? cameraToLowestPointDistance : cameraToSeaLevelDistance;
const cameraToLowestPointDistance = this.cameraToSeaLevelDistance - minElevation * this._pixelPerMeter / Math.cos(this._pitch);
const lowestPlane = minElevation < 0 ? cameraToLowestPointDistance : this.cameraToSeaLevelDistance;

// Find the distance from the center point [width/2 + offset.x, height/2 + offset.y] to the
// center top point [width/2 + offset.x, 0] in Z units, using the law of sines.
Expand Down Expand Up @@ -877,6 +905,20 @@ export class Transform {
this.projMatrix = m;
this.invProjMatrix = mat4.invert([] as any, m);

// create a fog matrix, same es proj-matrix but with near clipping-plane in mapcenter
// needed to calculate a correct z-value for fog calculation, because projMatrix z value is not
this.fogMatrix = new Float64Array(16) as any;
mat4.perspective(this.fogMatrix, this._fov, this.width / this.height, this.cameraToSeaLevelDistance, farZ);
this.fogMatrix[8] = -offset.x * 2 / this.width;
this.fogMatrix[9] = offset.y * 2 / this.height;
mat4.scale(this.fogMatrix, this.fogMatrix, [1, -1, 1]);
mat4.translate(this.fogMatrix, this.fogMatrix, [0, 0, -this.cameraToCenterDistance]);
mat4.rotateX(this.fogMatrix, this.fogMatrix, this._pitch);
mat4.rotateZ(this.fogMatrix, this.fogMatrix, this.angle);
mat4.translate(this.fogMatrix, this.fogMatrix, [-x, -y, 0]);
mat4.scale(this.fogMatrix, this.fogMatrix, [1, 1, this._pixelPerMeter]);
mat4.translate(this.fogMatrix, this.fogMatrix, [0, 0, -this.elevation]); // elevate camera over terrain

// matrix for conversion from world space to screen coordinates in 3D
this.pixelMatrix3D = mat4.multiply(new Float64Array(16) as any, this.labelPlaneMatrix, m);

Expand All @@ -901,6 +943,7 @@ export class Transform {

this._posMatrixCache = {};
this._alignedPosMatrixCache = {};
this._fogMatrixCache = {};
}

maxPitchScaleFactor() {
Expand Down
39 changes: 39 additions & 0 deletions src/render/draw_sky.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {StencilMode} from '../gl/stencil_mode';
import {DepthMode} from '../gl/depth_mode';
import {CullFaceMode} from '../gl/cull_face_mode';
import {PosArray, TriangleIndexArray} from '../data/array_types.g';
import posAttributes from '../data/pos_attributes';
import {SegmentVector} from '../data/segment';
import {skyUniformValues} from './program/sky_program';
import {Sky} from '../style/sky';
import type {Painter} from './painter';

export function drawSky(painter: Painter, sky: Sky) {
const context = painter.context;
const gl = context.gl;

const skyUniforms = skyUniformValues(sky, painter.style.map.transform, painter.pixelRatio);

const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
const stencilMode = StencilMode.disabled;
const colorMode = painter.colorModeForRenderPass();
const program = painter.useProgram('sky');

const vertexArray = new PosArray();
vertexArray.emplaceBack(-1, -1);
vertexArray.emplaceBack(1, -1);
vertexArray.emplaceBack(1, 1);
vertexArray.emplaceBack(-1, 1);

const indexArray = new TriangleIndexArray();
indexArray.emplaceBack(0, 1, 2);
indexArray.emplaceBack(0, 2, 3);

const vertexBuffer = context.createVertexBuffer(vertexArray, posAttributes.members);
const indexBuffer = context.createIndexBuffer(indexArray);
const segments = SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length);

program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode,
CullFaceMode.disabled, skyUniforms, undefined, 'sky', vertexBuffer,
indexBuffer, segments);
}
4 changes: 3 additions & 1 deletion src/render/draw_terrain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ function drawTerrain(painter: Painter, terrain: Terrain, tiles: Array<Tile>) {
context.activeTexture.set(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
const posMatrix = painter.transform.calculatePosMatrix(tile.tileID.toUnwrapped());
const uniformValues = terrainUniformValues(posMatrix, terrain.getMeshFrameDelta(painter.transform.zoom));
const eleDelta = terrain.getMeshFrameDelta(painter.transform.zoom);
const fogMatrix = painter.transform.calculateFogMatrix(tile.tileID.toUnwrapped());
const uniformValues = terrainUniformValues(posMatrix, eleDelta, fogMatrix, painter.style.sky, painter.transform.pitch);
program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, 'terrain', mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
}

Expand Down
4 changes: 4 additions & 0 deletions src/render/painter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import type {IndexBuffer} from '../gl/index_buffer';
import type {DepthRangeType, DepthMaskType, DepthFuncType} from '../gl/types';
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
import {RenderToTexture} from './render_to_texture';
import {drawSky} from './draw_sky';

export type RenderPass = 'offscreen' | 'opaque' | 'translucent';

Expand Down Expand Up @@ -415,6 +416,9 @@ export class Painter {
this.context.clear({color: options.showOverdrawInspector ? Color.black : Color.transparent, depth: 1});
this.clearStencil();

// draw sky first to not overwrite symbols
if (this.style.sky) drawSky(this, this.style.sky);

this._showOverdrawInspector = options.showOverdrawInspector;
this.depthRangeFor3D = [0, 1 - ((style._order.length + 2) * this.numSublayers * this.depthEpsilon)];

Expand Down
4 changes: 3 additions & 1 deletion src/render/program/program_uniforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {rasterUniforms} from './raster_program';
import {symbolIconUniforms, symbolSDFUniforms, symbolTextAndIconUniforms} from './symbol_program';
import {backgroundUniforms, backgroundPatternUniforms} from './background_program';
import {terrainUniforms, terrainDepthUniforms, terrainCoordsUniforms} from './terrain_program';
import {skyUniforms} from './sky_program';

export const programUniforms = {
fillExtrusion: fillExtrusionUniforms,
Expand Down Expand Up @@ -40,5 +41,6 @@ export const programUniforms = {
backgroundPattern: backgroundPatternUniforms,
terrain: terrainUniforms,
terrainDepth: terrainDepthUniforms,
terrainCoords: terrainCoordsUniforms
terrainCoords: terrainCoordsUniforms,
sky: skyUniforms
};
28 changes: 28 additions & 0 deletions src/render/program/sky_program.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {UniformColor, Uniform1f} from '../uniform_binding';
import type {Context} from '../../gl/context';
import type {UniformValues, UniformLocations} from '../uniform_binding';
import {Transform} from '../../geo/transform';
import {Sky} from '../../style/sky';

export type SkyUniformsType = {
'u_sky_color': UniformColor;
'u_fog_color': UniformColor;
'u_horizon': Uniform1f;
'u_horizon_blend': Uniform1f;
};

const skyUniforms = (context: Context, locations: UniformLocations): SkyUniformsType => ({
'u_sky_color': new UniformColor(context, locations.u_sky_color),
'u_fog_color': new UniformColor(context, locations.u_fog_color),
'u_horizon': new Uniform1f(context, locations.u_horizon),
'u_horizon_blend': new Uniform1f(context, locations.u_horizon_blend)
});

const skyUniformValues = (sky: Sky, transform: Transform, pixelRatio: number): UniformValues<SkyUniformsType> => ({
'u_sky_color': sky.properties.get('sky-color'),
'u_fog_color': sky.properties.get('fog-color'),
'u_horizon': (transform.height / 2 + transform.getHorizon()) * pixelRatio,
'u_horizon_blend': (sky.properties.get('horizon-blend') * transform.height / 2) * pixelRatio
});

export {skyUniforms, skyUniformValues};
27 changes: 22 additions & 5 deletions src/render/program/terrain_program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import {
Uniform1i,
Uniform1f,
Uniform4f,
UniformMatrix4f
UniformMatrix4f,
UniformColor
} from '../uniform_binding';
import type {Context} from '../../gl/context';
import type {UniformValues, UniformLocations} from '../../render/uniform_binding';
import {mat4} from 'gl-matrix';
import {Sky} from '../../style/sky';
import {Color} from '@maplibre/maplibre-gl-style-spec';

export type TerrainPreludeUniformsType = {
'u_depth': Uniform1i;
Expand All @@ -21,6 +24,10 @@ export type TerrainUniformsType = {
'u_matrix': UniformMatrix4f;
'u_texture': Uniform1i;
'u_ele_delta': Uniform1f;
'u_fog_matrix': UniformMatrix4f;
'u_fog_color': UniformColor;
'u_fog_blend': Uniform1f;
'u_fog_blend_opacity': Uniform1f;
};

export type TerrainDepthUniformsType = {
Expand All @@ -47,7 +54,11 @@ const terrainPreludeUniforms = (context: Context, locations: UniformLocations):
const terrainUniforms = (context: Context, locations: UniformLocations): TerrainUniformsType => ({
'u_matrix': new UniformMatrix4f(context, locations.u_matrix),
'u_texture': new Uniform1i(context, locations.u_texture),
'u_ele_delta': new Uniform1f(context, locations.u_ele_delta)
'u_ele_delta': new Uniform1f(context, locations.u_ele_delta),
'u_fog_matrix': new UniformMatrix4f(context, locations.u_fog_matrix),
'u_fog_color': new UniformColor(context, locations.u_fog_color),
'u_fog_blend': new Uniform1f(context, locations.u_fog_blend),
'u_fog_blend_opacity': new Uniform1f(context, locations.u_fog_blend_opacity)
});

const terrainDepthUniforms = (context: Context, locations: UniformLocations): TerrainDepthUniformsType => ({
Expand All @@ -64,11 +75,17 @@ const terrainCoordsUniforms = (context: Context, locations: UniformLocations): T

const terrainUniformValues = (
matrix: mat4,
eleDelta: number
): UniformValues<TerrainUniformsType> => ({
eleDelta: number,
fogMatrix: mat4,
sky: Sky,
pitch: number): UniformValues<TerrainUniformsType> => ({
'u_matrix': matrix,
'u_texture': 0,
'u_ele_delta': eleDelta
'u_ele_delta': eleDelta,
'u_fog_matrix': fogMatrix,
'u_fog_color': sky ? sky.properties.get('fog-color') : Color.white,
'u_fog_blend': sky ? sky.properties.get('fog-blend') : 1,
'u_fog_blend_opacity': sky ? sky.calculateFogBlendOpacity(pitch) : 0
});

const terrainDepthUniformValues = (
Expand Down
2 changes: 1 addition & 1 deletion src/render/render_to_texture.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('render to texture', () => {
const painter = {
layersDrawn: 0,
context: new Context(gl),
transform: {zoom: 10, calculatePosMatrix: () => {}},
transform: {zoom: 10, calculatePosMatrix: () => {}, calculateFogMatrix: () => {}},
colorModeForRenderPass: () => ColorMode.alphaBlended,
useProgram: () => { return {draw: () => { layersDrawn++; }}; },
_renderTileClippingMasks: () => {},
Expand Down
10 changes: 7 additions & 3 deletions src/shaders/shaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ import symbolTextAndIconVert from './symbol_text_and_icon.vertex.glsl.g';
import terrainDepthFrag from './terrain_depth.fragment.glsl.g';
import terrainCoordsFrag from './terrain_coords.fragment.glsl.g';
import terrainFrag from './terrain.fragment.glsl.g';
import terrainDepthVert from './terrain_depth.vertex.glsl.g';
import terrainCoordsVert from './terrain_coords.vertex.glsl.g';
import terrainVert from './terrain.vertex.glsl.g';
import skyFrag from './sky.fragment.glsl.g';
import skyVert from './sky.vertex.glsl.g';

export const shaders = {
prelude: compile(preludeFrag, preludeVert),
Expand Down Expand Up @@ -86,9 +90,9 @@ export const shaders = {
symbolSDF: compile(symbolSDFFrag, symbolSDFVert),
symbolTextAndIcon: compile(symbolTextAndIconFrag, symbolTextAndIconVert),
terrain: compile(terrainFrag, terrainVert),
terrainDepth: compile(terrainDepthFrag, terrainVert),
terrainCoords: compile(terrainCoordsFrag, terrainVert)
};
terrainDepth: compile(terrainDepthFrag, terrainDepthVert),
terrainCoords: compile(terrainCoordsFrag, terrainCoordsVert),
sky: compile(skyFrag, skyVert)};

// Expand #pragmas to #ifdefs.

Expand Down
18 changes: 18 additions & 0 deletions src/shaders/sky.fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
uniform vec4 u_sky_color;
uniform vec4 u_fog_color;
uniform float u_horizon;
uniform float u_horizon_blend;

void main() {
float y = gl_FragCoord.y;
if (y > u_horizon) {
float blend = y - u_horizon;
if (blend < u_horizon_blend) {
gl_FragColor = mix(u_sky_color, u_fog_color, pow(1.0 - blend / u_horizon_blend, 2.0));
} else {
gl_FragColor = u_sky_color;
}
} else {
gl_FragColor = u_fog_color;
}
}
5 changes: 5 additions & 0 deletions src/shaders/sky.vertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
attribute vec2 a_pos;

void main() {
gl_Position = vec4(a_pos, 1.0, 1.0);
}
12 changes: 11 additions & 1 deletion src/shaders/terrain.fragment.glsl
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
uniform sampler2D u_texture;
uniform vec4 u_fog_color;
uniform float u_fog_blend;
uniform float u_fog_blend_opacity;

in vec2 v_texture_pos;
in float v_fog_depth;

void main() {
fragColor = texture(u_texture, v_texture_pos);
vec4 color = texture2D(u_texture, v_texture_pos);
if (v_fog_depth > u_fog_blend) {
float a = (v_fog_depth - u_fog_blend) / (1.0 - u_fog_blend);
gl_FragColor = mix(color, u_fog_color, pow(a * u_fog_blend_opacity, 2.0));
} else {
gl_FragColor = color;
}
}
Loading
Loading