Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
e09c40f
inplement GDAL's basic hillshade algorithm
NathanMOlson Mar 26, 2025
f9503ed
add new hillshade algorithms
NathanMOlson Mar 26, 2025
736fa20
set hillshade method based on map style
NathanMOlson Mar 27, 2025
117b328
Merge branch 'main' into hillshade
NathanMOlson Apr 1, 2025
889c14c
add illumination altitude and basic method
NathanMOlson Apr 1, 2025
6e173be
remove unrelated changes
NathanMOlson Apr 1, 2025
d7ca866
support multidirectional hillshade
NathanMOlson Apr 1, 2025
ce5634a
fix "combined"
NathanMOlson Apr 2, 2025
0b8fc31
improve how combined uses shadows and highlights
NathanMOlson Apr 2, 2025
c7a6e73
add uniform binding for float array and uniform binding unit tests
NathanMOlson Apr 3, 2025
a04354b
fix basic hillshade color blending
NathanMOlson Apr 4, 2025
07ee865
make hillshade properly depend on tileSize
NathanMOlson Apr 4, 2025
bf6a3ec
fix lint
NathanMOlson Apr 5, 2025
c5d1938
Merge branch 'main' into hillshade
NathanMOlson Apr 5, 2025
b64f358
fix UniformFloatArray
NathanMOlson Apr 13, 2025
defdbce
support new style spec definitions
NathanMOlson Apr 15, 2025
5df1d9d
Merge branch 'main' into hillshade
NathanMOlson Apr 15, 2025
38fd4f3
support new style spec definitions
NathanMOlson Apr 15, 2025
ee0c348
update types to camelCase
NathanMOlson Apr 15, 2025
c355b06
Merge branch 'main' into hillshade
NathanMOlson Apr 15, 2025
80eaefa
Merge branch 'hillshade_new_types' into hillshade
NathanMOlson Apr 15, 2025
f28a82a
hook up new hillshade style spec
NathanMOlson Apr 15, 2025
0828c75
throw error if hillshade illumination properties are empty array.
NathanMOlson Apr 15, 2025
34c7bff
add hillshade style layer tests
NathanMOlson Apr 16, 2025
4c25277
add hilshade style layer tests
NathanMOlson Apr 16, 2025
d94b46d
lint
NathanMOlson Apr 16, 2025
ebf547f
Merge branch 'main' into hillshade
NathanMOlson Apr 16, 2025
e6e1d3b
fix test warnings
NathanMOlson Apr 16, 2025
432d0fa
fix unit test
NathanMOlson Apr 16, 2025
a78a881
add render tests for new hillshade methods
NathanMOlson Apr 16, 2025
40f5794
multidirectional hillshade render test
NathanMOlson Apr 16, 2025
8c831b5
hook up hillshade-exaggeration to hew hillshade methods
NathanMOlson Apr 17, 2025
dc4e764
add hillshade example
NathanMOlson Apr 17, 2025
497bfda
Merge branch 'main' into hillshade
NathanMOlson Apr 17, 2025
6724235
update changelog
NathanMOlson Apr 17, 2025
26ed533
add comments in shader
NathanMOlson Apr 17, 2025
6040cb4
update to latest version of style spec
NathanMOlson Apr 23, 2025
1f1b669
Merge branch 'main' into hillshade_new_types
NathanMOlson Apr 23, 2025
bf6443d
fix style-spec integrity check
NathanMOlson Apr 23, 2025
b6923c7
increase expected bytes
NathanMOlson Apr 23, 2025
999703f
Merge branch 'hillshade_new_types' into hillshade
NathanMOlson Apr 23, 2025
b68b5d2
update expectedBytes
NathanMOlson Apr 23, 2025
e35546d
update failing render tests
NathanMOlson Apr 23, 2025
dd8cbfa
change if/else to switch/case
NathanMOlson Apr 23, 2025
e23599c
remove terrain from hillshade example
NathanMOlson Apr 23, 2025
0f3e725
change hillshade method to switch/case
NathanMOlson Apr 23, 2025
578659e
split unit test
NathanMOlson Apr 23, 2025
3f385f6
replace defines null option with default empty array.
NathanMOlson Apr 23, 2025
80f7d3d
Add new, simple hillshade demo.
NathanMOlson Apr 23, 2025
1098db0
Add GDAL links in hillshade comments
NathanMOlson Apr 23, 2025
4c83596
remove empty array check in HillshadeStyleLayer. Will be handled in s…
NathanMOlson Apr 23, 2025
a8f8d36
fix unit test
NathanMOlson Apr 23, 2025
e7f7328
update changelog to highlight rendering change for hillshade layers w…
NathanMOlson Apr 23, 2025
fa8939f
add multidirectional hillshade example
NathanMOlson Apr 23, 2025
471b89a
remove hillshade style exploration example
NathanMOlson Apr 23, 2025
2c2eaf6
update style spec version
NathanMOlson Apr 24, 2025
4158e3e
replace for loop with map
NathanMOlson Apr 24, 2025
028b844
replace for loops with Array fill and concat
NathanMOlson Apr 24, 2025
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
## main

### ✨ Features and improvements
- Add additional hillshade methods ([#5768](https://github.com/maplibre/maplibre-gl-js/pull/5768))
- _...Add new stuff here..._

### 🐞 Bug fixes
- Fix scroll min zoom on globe view ([#5775](https://github.com/maplibre/maplibre-gl-js/pull/5775))
- ⚠️ Fix hillshade appearance change between 256x256 and 512x512 tiles. This will change the appearance of hillshade layers using 512x512 tiles. ([#5768](https://github.com/maplibre/maplibre-gl-js/pull/5768))
- _...Add new stuff here..._

## 5.4.0
Expand Down
6 changes: 5 additions & 1 deletion build/generate-style-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ function nativeType(property) {
return 'Color';
case 'padding':
return 'Padding';
case 'numberArray':
return 'NumberArray';
case 'colorArray':
return 'ColorArray';
case 'variableAnchorOffsetCollection':
return 'VariableAnchorOffsetCollection';
case 'sprite':
Expand Down Expand Up @@ -186,7 +190,7 @@ import {
CrossFaded
} from '../properties';

import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
import type {Color, Formatted, Padding, NumberArray, ColorArray, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
`);

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/examples/hillshade-simple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^1.3.1",
"@mapbox/whoots-js": "^3.1.0",
"@maplibre/maplibre-gl-style-spec": "^23.1.0",
"@maplibre/maplibre-gl-style-spec": "^23.2.1",
"@types/geojson": "^7946.0.16",
"@types/geojson-vt": "3.2.5",
"@types/mapbox__point-geometry": "^0.1.4",
Expand Down
4 changes: 3 additions & 1 deletion src/render/draw_hillshade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ function renderHillshade(
const context = painter.context;
const transform = painter.transform;
const gl = context.gl;
const program = painter.useProgram('hillshade');

const defines = [`#define NUM_ILLUMINATION_SOURCES ${layer.paint.get('hillshade-highlight-color').values.length}`];
const program = painter.useProgram('hillshade', null, false, defines);
const align = !painter.options.moving;

for (const coord of coords) {
Expand Down
10 changes: 6 additions & 4 deletions src/render/painter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -711,12 +711,12 @@ export class Painter {
* Finds the required shader and its variant (base/terrain/globe, etc.) and binds it, compiling a new shader if required.
* @param name - Name of the desired shader.
* @param programConfiguration - Configuration of shader's inputs.
* @param defines - Additional macros to be injected at the beginning of the shader. Expected format is `['#define XYZ']`, etc.
* @param forceSimpleProjection - Whether to force the use of a shader variant with simple mercator projection vertex shader.
* @param defines - Additional macros to be injected at the beginning of the shader. Expected format is `['#define XYZ']`, etc.
* False by default. Use true when drawing with a simple projection matrix is desired, eg. when drawing a fullscreen quad.
* @returns
*/
useProgram(name: string, programConfiguration?: ProgramConfiguration | null, forceSimpleProjection: boolean = false): Program<any> {
useProgram(name: string, programConfiguration?: ProgramConfiguration | null, forceSimpleProjection: boolean = false, defines: Array<string> = []): Program<any> {
this.cache = this.cache || {};
const useTerrain = !!this.style.map.terrain;

Expand All @@ -729,8 +729,9 @@ export class Painter {
const configurationKey = (programConfiguration ? programConfiguration.cacheKey : '');
const overdrawKey = (this._showOverdrawInspector ? '/overdraw' : '');
const terrainKey = (useTerrain ? '/terrain' : '');
const definesKey = (defines ? `/${defines.join('/')}` : '');

const key = name + configurationKey + projectionKey + overdrawKey + terrainKey;
const key = name + configurationKey + projectionKey + overdrawKey + terrainKey + definesKey;

if (!this.cache[key]) {
this.cache[key] = new Program(
Expand All @@ -741,7 +742,8 @@ export class Painter {
this._showOverdrawInspector,
useTerrain,
projectionPrelude,
projectionDefine
projectionDefine,
defines
);
}
return this.cache[key];
Expand Down
6 changes: 5 additions & 1 deletion src/render/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export class Program<Us extends UniformBindings> {
showOverdrawInspector: boolean,
hasTerrain: boolean,
projectionPrelude: PreparedShader,
projectionDefine: string) {
projectionDefine: string,
extraDefines: Array<string> = []) {

const gl = context.gl;
this.program = gl.createProgram();
Expand Down Expand Up @@ -85,6 +86,9 @@ export class Program<Us extends UniformBindings> {
if (projectionDefine) {
defines.push(projectionDefine);
}
if (extraDefines) {
defines.push(...extraDefines);
}

let fragmentSource = defines.concat(shaders.prelude.fragmentSource, projectionPrelude.fragmentSource, source.fragmentSource).join('\n');
let vertexSource = defines.concat(shaders.prelude.vertexSource, projectionPrelude.vertexSource, source.vertexSource).join('\n');
Expand Down
65 changes: 48 additions & 17 deletions src/render/program/hillshade_program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
Uniform1f,
Uniform2f,
UniformColor,
UniformFloatArray,
UniformColorArray,
UniformMatrix4f,
Uniform4f
} from '../uniform_binding';
Expand All @@ -22,10 +24,13 @@ import type {OverscaledTileID} from '../../source/tile_id';
export type HillshadeUniformsType = {
'u_image': Uniform1i;
'u_latrange': Uniform2f;
'u_light': Uniform2f;
'u_shadow': UniformColor;
'u_highlight': UniformColor;
'u_exaggeration': Uniform1f;
'u_altitudes': UniformFloatArray;
'u_azimuths': UniformFloatArray;
'u_accent': UniformColor;
'u_method': Uniform1i;
'u_shadows': UniformColorArray;
'u_highlights': UniformColorArray;
};

export type HillshadePrepareUniformsType = {
Expand All @@ -39,10 +44,13 @@ export type HillshadePrepareUniformsType = {
const hillshadeUniforms = (context: Context, locations: UniformLocations): HillshadeUniformsType => ({
'u_image': new Uniform1i(context, locations.u_image),
'u_latrange': new Uniform2f(context, locations.u_latrange),
'u_light': new Uniform2f(context, locations.u_light),
'u_shadow': new UniformColor(context, locations.u_shadow),
'u_highlight': new UniformColor(context, locations.u_highlight),
'u_accent': new UniformColor(context, locations.u_accent)
'u_exaggeration': new Uniform1f(context, locations.u_exaggeration),
'u_altitudes': new UniformFloatArray(context, locations.u_altitudes),
'u_azimuths': new UniformFloatArray(context, locations.u_azimuths),
'u_accent': new UniformColor(context, locations.u_accent),
'u_method': new Uniform1i(context, locations.u_method),
'u_shadows': new UniformColorArray(context, locations.u_shadows),
'u_highlights': new UniformColorArray(context, locations.u_highlights)
});

const hillshadePrepareUniforms = (context: Context, locations: UniformLocations): HillshadePrepareUniformsType => ({
Expand All @@ -58,22 +66,45 @@ const hillshadeUniformValues = (
tile: Tile,
layer: HillshadeStyleLayer,
): UniformValues<HillshadeUniformsType> => {
const shadow = layer.paint.get('hillshade-shadow-color');
const highlight = layer.paint.get('hillshade-highlight-color');
const accent = layer.paint.get('hillshade-accent-color');
let method;
switch (layer.paint.get('hillshade-method')) {
case 'basic':
method = 4;
break;
case 'combined':
method = 1;
break;
case 'igor':
method = 2;
break;
case 'multidirectional':
method = 3;
break;
case 'standard':
default:
method = 0;
break;
}

const illumination = layer.getIlluminationProperties();

let azimuthal = layer.paint.get('hillshade-illumination-direction') * (Math.PI / 180);
// modify azimuthal angle by map rotation if light is anchored at the viewport
if (layer.paint.get('hillshade-illumination-anchor') === 'viewport') {
azimuthal += painter.transform.bearingInRadians;
for (let i = 0; i < illumination.directionRadians.length; i++) {
// modify azimuthal angle by map rotation if light is anchored at the viewport
if (layer.paint.get('hillshade-illumination-anchor') === 'viewport') {
illumination.directionRadians[i] += painter.transform.bearingInRadians;
}
}
return {
'u_image': 0,
'u_latrange': getTileLatRange(painter, tile.tileID),
'u_light': [layer.paint.get('hillshade-exaggeration'), azimuthal],
'u_shadow': shadow,
'u_highlight': highlight,
'u_accent': accent
'u_exaggeration': layer.paint.get('hillshade-exaggeration'),
'u_altitudes': illumination.altitudeRadians,
'u_azimuths': illumination.directionRadians,
'u_accent': accent,
'u_method': method,
'u_highlights': illumination.highlightColor,
'u_shadows': illumination.shadowColor
};
};

Expand Down
39 changes: 39 additions & 0 deletions src/render/uniform_binding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import {
Uniform2f,
Uniform3f,
Uniform4f,
UniformFloatArray,
UniformColorArray,
UniformMatrix4f
} from './uniform_binding';
import {Color} from '@maplibre/maplibre-gl-style-spec';

describe('Uniform Binding', () => {
test('Uniform1i', () => {
Expand Down Expand Up @@ -120,4 +123,40 @@ describe('Uniform Binding', () => {
u.set([2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
});

test('UniformColorArray', () => {
expect.assertions(4);

const context = {
gl: {
uniform4fv: () => { expect(true).toBeTruthy(); }
}
} as any as Context;

const u = new UniformColorArray(context, 0);
expect(u.current).toEqual(new Array<Color>);
const v = [new Color(0.1, 0.2, 0.3), new Color(0.7, 0.8, 0.9)];
u.set(v);
expect(u.current).toEqual(v);
u.set(v);
u.set([new Color(0.3, 0.4, 0.5), new Color(0.4, 0.5, 0.6), new Color(0.5, 0.6, 0.7)]);
});

test('UniformFloatArray', () => {
expect.assertions(4);

const context = {
gl: {
uniform1fv: () => { expect(true).toBeTruthy(); }
}
} as any as Context;

const u = new UniformFloatArray(context, 0);
expect(u.current).toEqual(new Array<number>);
const v = [1.2, 3.4];
u.set(v);
expect(u.current).toEqual(v);
u.set(v);
u.set([5.6, 7.8, 9.1]);
});

});
38 changes: 38 additions & 0 deletions src/render/uniform_binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,42 @@ class UniformColor extends Uniform<Color> {
}
}

class UniformColorArray extends Uniform<Array<Color>> {
constructor(context: Context, location: WebGLUniformLocation) {
super(context, location);
this.current = new Array<Color>();
}

set(v: Array<Color>): void {
if (v != this.current) {
this.current = v;
const values = new Float32Array(v.length*4);
for( let i = 0; i < v.length; i++) {
values[4*i] = v[i].r;
values[4*i+1] = v[i].g;
values[4*i+2] = v[i].b;
values[4*i+3] = v[i].a;
}
this.gl.uniform4fv(this.location, values);
}
}
}

class UniformFloatArray extends Uniform<Array<number>> {
constructor(context: Context, location: WebGLUniformLocation) {
super(context, location);
this.current = new Array<number>();
}

set(v: Array<number>): void {
if (v != this.current) {
this.current = v;
const values = new Float32Array(v);
this.gl.uniform1fv(this.location, values);
}
}
}

const emptyMat4 = new Float32Array(16) as mat4;
class UniformMatrix4f extends Uniform<mat4> {
constructor(context: Context, location: WebGLUniformLocation) {
Expand Down Expand Up @@ -147,6 +183,8 @@ export {
Uniform3f,
Uniform4f,
UniformColor,
UniformColorArray,
UniformFloatArray,
UniformMatrix4f
};

Expand Down
Loading