core: Update Hillshade Algorithms and add Color-Relief Layer support (#3965)

This commit is contained in:
Andrew Calcutt
2026-02-25 20:50:06 -05:00
committed by GitHub
parent 9682e09ae4
commit 369af13ead
187 changed files with 8804 additions and 2352 deletions

View File

@@ -106,11 +106,11 @@ jobs:
persist-credentials: false
- name: Get OS Architecture
if: runner.os == 'MacOS' || runner.os == 'Linux'
if: runner.os == 'macOS' || runner.os == 'Linux'
run: uname -m
- name: Install dependencies (MacOS)
if: runner.os == 'MacOS'
if: runner.os == 'macOS'
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
@@ -200,7 +200,7 @@ jobs:
key: ${{ runner.os }}-${{ runner.arch }}-cmake-node-module-deps
- name: Configure maplibre-native (MacOS)
if: runner.os == 'MacOS'
if: runner.os == 'macOS'
run: |
cmake --preset macos-metal-node -DCMAKE_BUILD_TYPE=${{ env.BUILDTYPE }}
@@ -242,7 +242,7 @@ jobs:
cmake --preset windows-arm64-opengl-node -DCMAKE_BUILD_TYPE=${{ env.BUILDTYPE }}
- name: Build maplibre-native (MacOS/Linux)
if: runner.os == 'MacOS' || runner.os == 'Linux'
if: runner.os == 'macOS' || runner.os == 'Linux'
run: |
cmake --build build -j "$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null)"
@@ -254,7 +254,7 @@ jobs:
- name: Run render tests on macOS
id: render_tests
if: runner.os == 'macOS'
continue-on-error: ${{ runner.arch == 'X64' }}
continue-on-error: true
run: ./build/mbgl-render-test-runner --manifestPath metrics/macos-xcode11-release-style.json
- name: Upload render test artifacts (MacOS)
@@ -263,6 +263,11 @@ jobs:
with:
name: render-query-test-results_${{ runner.os }}_${{ matrix.arch }}
path: metrics/macos-xcode11-release-style.html
if-no-files-found: ignore
- name: Fail macOS render tests when required
if: runner.os == 'macOS' && steps.render_tests.outcome == 'failure' && runner.arch != 'X64'
run: exit 1
- name: Test (Linux)
working-directory: platform/node
@@ -270,7 +275,7 @@ jobs:
run: xvfb-run --auto-servernum npm test
- name: Test (MacOS)
if: runner.os == 'MacOS'
if: runner.os == 'macOS'
continue-on-error: true # flaky on CI
working-directory: platform/node
run: npm test

View File

@@ -172,6 +172,7 @@ list(APPEND SRC_FILES
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/dynamic_texture_atlas.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/gpu_expression.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/hillshade_prepare_drawable_data.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/color_relief_drawable_data.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/image_drawable_data.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/line_drawable_data.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/gfx/symbol_drawable_data.hpp
@@ -251,6 +252,7 @@ list(APPEND INCLUDE_FILES
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/fill_layer_factory.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/heatmap_layer_factory.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/hillshade_layer_factory.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/color_relief_layer_factory.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/layer_factory.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/layer_manager.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/line_layer_factory.hpp
@@ -362,6 +364,7 @@ list(APPEND INCLUDE_FILES
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/fill_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/heatmap_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/hillshade_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/color_relief_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/line_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/location_indicator_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/raster_layer.hpp
@@ -501,6 +504,7 @@ list(APPEND SRC_FILES
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/fill_layer_factory.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/heatmap_layer_factory.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/hillshade_layer_factory.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/color_relief_layer_factory.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/layer_factory.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/layer_manager.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/line_layer_factory.cpp
@@ -582,6 +586,10 @@ list(APPEND SRC_FILES
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_heatmap_layer.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_hillshade_layer.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_hillshade_layer.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_color_relief_layer.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_color_relief_layer.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/color_relief_layer_tweaker.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/color_relief_layer_tweaker.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_line_layer.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_line_layer.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -680,6 +688,7 @@ list(APPEND SRC_FILES
${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/light.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/position.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/property_value.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/hillshade_conversions.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/rotation.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/source.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/source_options.cpp
@@ -765,6 +774,11 @@ list(APPEND SRC_FILES
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer_impl.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer_properties.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer_properties.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/color_relief_layer.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/color_relief_layer_impl.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/color_relief_layer_impl.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/color_relief_layer_properties.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/color_relief_layer_properties.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer_impl.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer_impl.hpp

View File

@@ -23,6 +23,7 @@ MLN_PUBLIC_GENERATED_STYLE_HEADERS = [
"include/mbgl/style/layers/fill_extrusion_layer.hpp",
"include/mbgl/style/layers/raster_layer.hpp",
"include/mbgl/style/layers/hillshade_layer.hpp",
"include/mbgl/style/layers/color_relief_layer.hpp",
"include/mbgl/style/layers/background_layer.hpp",
"include/mbgl/style/layers/location_indicator_layer.hpp",
"include/mbgl/style/light.hpp",
@@ -37,6 +38,7 @@ MLN_PRIVATE_GENERATED_STYLE_HEADERS = [
"src/mbgl/style/layers/fill_extrusion_layer_properties.hpp",
"src/mbgl/style/layers/raster_layer_properties.hpp",
"src/mbgl/style/layers/hillshade_layer_properties.hpp",
"src/mbgl/style/layers/color_relief_layer_properties.hpp",
"src/mbgl/style/layers/background_layer_properties.hpp",
"src/mbgl/style/layers/location_indicator_layer_properties.hpp",
]
@@ -58,6 +60,8 @@ MLN_GENERATED_STYLE_SOURCE = [
"src/mbgl/style/layers/raster_layer.cpp",
"src/mbgl/style/layers/hillshade_layer_properties.cpp",
"src/mbgl/style/layers/hillshade_layer.cpp",
"src/mbgl/style/layers/color_relief_layer_properties.cpp",
"src/mbgl/style/layers/color_relief_layer.cpp",
"src/mbgl/style/layers/background_layer_properties.cpp",
"src/mbgl/style/layers/background_layer.cpp",
"src/mbgl/style/layers/location_indicator_layer_properties.cpp",
@@ -95,6 +99,7 @@ MLN_GENERATED_OPENGL_SHADER_HEADERS = [
"include/mbgl/shaders/gl/heatmap_texture.hpp",
"include/mbgl/shaders/gl/hillshade_prepare.hpp",
"include/mbgl/shaders/gl/hillshade.hpp",
"include/mbgl/shaders/gl/color_relief.hpp",
"include/mbgl/shaders/gl/line_gradient.hpp",
"include/mbgl/shaders/gl/line_pattern.hpp",
"include/mbgl/shaders/gl/line_sdf.hpp",
@@ -161,6 +166,7 @@ MLN_CORE_SOURCE = [
"src/mbgl/layermanager/fill_layer_factory.cpp",
"src/mbgl/layermanager/heatmap_layer_factory.cpp",
"src/mbgl/layermanager/hillshade_layer_factory.cpp",
"src/mbgl/layermanager/color_relief_layer_factory.cpp",
"src/mbgl/layermanager/layer_factory.cpp",
"src/mbgl/layermanager/layer_manager.cpp",
"src/mbgl/layermanager/line_layer_factory.cpp",
@@ -237,6 +243,8 @@ MLN_CORE_SOURCE = [
"src/mbgl/renderer/layers/render_heatmap_layer.hpp",
"src/mbgl/renderer/layers/render_hillshade_layer.cpp",
"src/mbgl/renderer/layers/render_hillshade_layer.hpp",
"src/mbgl/renderer/layers/render_color_relief_layer.cpp",
"src/mbgl/renderer/layers/render_color_relief_layer.hpp",
"src/mbgl/renderer/layers/render_line_layer.cpp",
"src/mbgl/renderer/layers/render_line_layer.hpp",
"src/mbgl/renderer/layers/render_location_indicator_layer.cpp",
@@ -336,6 +344,7 @@ MLN_CORE_SOURCE = [
"src/mbgl/style/conversion/light.cpp",
"src/mbgl/style/conversion/position.cpp",
"src/mbgl/style/conversion/property_value.cpp",
"src/mbgl/style/conversion/hillshade_conversions.cpp",
"src/mbgl/style/conversion/rotation.cpp",
"src/mbgl/style/conversion/source.cpp",
"src/mbgl/style/conversion/source_options.cpp",
@@ -409,6 +418,8 @@ MLN_CORE_SOURCE = [
"src/mbgl/style/layers/heatmap_layer_impl.hpp",
"src/mbgl/style/layers/hillshade_layer_impl.cpp",
"src/mbgl/style/layers/hillshade_layer_impl.hpp",
"src/mbgl/style/layers/color_relief_layer_impl.cpp",
"src/mbgl/style/layers/color_relief_layer_impl.hpp",
"src/mbgl/style/layers/line_layer_impl.cpp",
"src/mbgl/style/layers/line_layer_impl.hpp",
"src/mbgl/style/layers/location_indicator_layer_impl.cpp",
@@ -652,6 +663,7 @@ MLN_CORE_HEADERS = [
"include/mbgl/layermanager/fill_layer_factory.hpp",
"include/mbgl/layermanager/heatmap_layer_factory.hpp",
"include/mbgl/layermanager/hillshade_layer_factory.hpp",
"include/mbgl/layermanager/color_relief_layer_factory.hpp",
"include/mbgl/layermanager/layer_factory.hpp",
"include/mbgl/layermanager/layer_manager.hpp",
"include/mbgl/layermanager/line_layer_factory.hpp",
@@ -912,6 +924,7 @@ MLN_DRAWABLES_SOURCE = [
"src/mbgl/gfx/dynamic_texture_atlas.cpp",
"src/mbgl/gfx/gpu_expression.cpp",
"src/mbgl/gfx/hillshade_prepare_drawable_data.hpp",
"src/mbgl/gfx/color_relief_drawable_data.hpp",
"src/mbgl/gfx/image_drawable_data.hpp",
"src/mbgl/gfx/line_drawable_data.hpp",
"src/mbgl/gfx/symbol_drawable_data.hpp",
@@ -927,6 +940,8 @@ MLN_DRAWABLES_SOURCE = [
"src/mbgl/renderer/layers/background_layer_tweaker.hpp",
"src/mbgl/renderer/layers/circle_layer_tweaker.cpp",
"src/mbgl/renderer/layers/circle_layer_tweaker.hpp",
"src/mbgl/renderer/layers/color_relief_layer_tweaker.cpp",
"src/mbgl/renderer/layers/color_relief_layer_tweaker.hpp",
"src/mbgl/renderer/layers/fill_layer_tweaker.cpp",
"src/mbgl/renderer/layers/fill_layer_tweaker.hpp",
"src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp",
@@ -987,6 +1002,7 @@ MLN_DRAWABLES_HEADERS = [
"include/mbgl/shaders/heatmap_layer_ubo.hpp",
"include/mbgl/shaders/heatmap_texture_layer_ubo.hpp",
"include/mbgl/shaders/hillshade_layer_ubo.hpp",
"include/mbgl/shaders/color_relief_layer_ubo.hpp",
"include/mbgl/shaders/hillshade_prepare_layer_ubo.hpp",
"include/mbgl/shaders/layer_ubo.hpp",
"include/mbgl/shaders/line_layer_ubo.hpp",
@@ -1063,6 +1079,7 @@ MLN_DRAWABLES_MTL_SOURCE = [
"src/mbgl/shaders/mtl/heatmap_texture.cpp",
"src/mbgl/shaders/mtl/hillshade.cpp",
"src/mbgl/shaders/mtl/hillshade_prepare.cpp",
"src/mbgl/shaders/mtl/color_relief.cpp",
"src/mbgl/shaders/mtl/line.cpp",
"src/mbgl/shaders/mtl/location_indicator.cpp",
"src/mbgl/shaders/mtl/raster.cpp",
@@ -1105,6 +1122,7 @@ MLN_DRAWABLES_MTL_HEADERS = [
"include/mbgl/shaders/mtl/heatmap_texture.hpp",
"include/mbgl/shaders/mtl/hillshade.hpp",
"include/mbgl/shaders/mtl/hillshade_prepare.hpp",
"include/mbgl/shaders/mtl/color_relief.hpp",
"include/mbgl/shaders/mtl/line.hpp",
"include/mbgl/shaders/mtl/location_indicator.hpp",
"include/mbgl/shaders/mtl/raster.hpp",
@@ -1150,6 +1168,7 @@ MLN_DRAWABLES_WEBGPU_SOURCE = [
"src/mbgl/shaders/webgpu/heatmap_texture.cpp",
"src/mbgl/shaders/webgpu/hillshade.cpp",
"src/mbgl/shaders/webgpu/hillshade_prepare.cpp",
"src/mbgl/shaders/webgpu/color_relief.cpp",
"src/mbgl/shaders/webgpu/line.cpp",
"src/mbgl/shaders/webgpu/location_indicator.cpp",
"src/mbgl/shaders/webgpu/raster.cpp",
@@ -1192,6 +1211,7 @@ MLN_DRAWABLES_WEBGPU_HEADERS = [
"include/mbgl/shaders/webgpu/heatmap_texture.hpp",
"include/mbgl/shaders/webgpu/hillshade.hpp",
"include/mbgl/shaders/webgpu/hillshade_prepare.hpp",
"include/mbgl/shaders/webgpu/color_relief.hpp",
"include/mbgl/shaders/webgpu/line.hpp",
"include/mbgl/shaders/webgpu/location_indicator.hpp",
"include/mbgl/shaders/webgpu/raster.hpp",

View File

@@ -44,6 +44,7 @@ list(APPEND
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/heatmap_texture.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/hillshade.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/hillshade_prepare.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/color_relief.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/line.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/location_indicator.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/mtl/raster.hpp
@@ -86,6 +87,7 @@ list(APPEND
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/heatmap_texture.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/hillshade.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/hillshade_prepare.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/color_relief.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/line.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/location_indicator.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/raster.cpp

View File

@@ -37,6 +37,7 @@ list(APPEND
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/heatmap_texture.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/hillshade_prepare.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/hillshade.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/color_relief.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/line.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/line_gradient.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/line_pattern.hpp
@@ -113,6 +114,7 @@ list(APPEND INCLUDE_FILES
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/heatmap_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/heatmap_texture_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/hillshade_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/color_relief_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/hillshade_prepare_layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/layer_ubo.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/line_layer_ubo.hpp

View File

@@ -47,6 +47,7 @@ list(APPEND
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/heatmap_texture.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/hillshade.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/hillshade_prepare.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/color_relief.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/line.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/location_indicator.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/vulkan/raster.hpp
@@ -91,6 +92,7 @@ list(APPEND
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/heatmap_texture.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/hillshade.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/hillshade_prepare.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/color_relief.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/line.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/location_indicator.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/vulkan/raster.cpp

View File

@@ -36,6 +36,7 @@ list(APPEND
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/heatmap_texture.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/hillshade.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/hillshade_prepare.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/color_relief.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/line.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/location_indicator.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/raster.cpp
@@ -75,6 +76,7 @@ list(APPEND
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/heatmap_texture.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/hillshade.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/hillshade_prepare.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/color_relief.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/line.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/location_indicator.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/raster.hpp

View File

@@ -0,0 +1,15 @@
#pragma once
#include <mbgl/layermanager/layer_factory.hpp>
namespace mbgl {
class ColorReliefLayerFactory : public LayerFactory {
protected:
const style::LayerTypeInfo* getTypeInfo() const noexcept final;
std::unique_ptr<style::Layer> createLayer(const std::string& id,
const style::conversion::Convertible& value) noexcept final;
std::unique_ptr<RenderLayer> createRenderLayer(Immutable<style::Layer::Impl>) noexcept final;
};
} // namespace mbgl

View File

@@ -0,0 +1,32 @@
#pragma once
#include <mbgl/shaders/layer_ubo.hpp>
namespace mbgl {
namespace shaders {
struct alignas(16) ColorReliefDrawableUBO {
/* 0 */ std::array<float, 4 * 4> matrix;
/* 64 */
};
static_assert(sizeof(ColorReliefDrawableUBO) == 64);
struct alignas(16) ColorReliefTilePropsUBO {
/* 0 */ std::array<float, 4> unpack; // DEM unpack vector
/* 16 */ std::array<float, 2> dimension; // Texture dimensions
/* 24 */ int32_t color_ramp_size; // Number of color stops
/* 28 */ float pad_tile0; // Padding for alignment
/* 32 */
};
static_assert(sizeof(ColorReliefTilePropsUBO) == 32);
struct alignas(16) ColorReliefEvaluatedPropsUBO {
/* 0 */ float opacity;
/* 4 */ float pad_eval0;
/* 8 */ float pad_eval1;
/* 12 */ float pad_eval2;
/* 16 */
};
static_assert(sizeof(ColorReliefEvaluatedPropsUBO) == 16);
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,119 @@
// Generated code, do not modify this file!
#pragma once
#include <mbgl/shaders/shader_source.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::ColorReliefShader, gfx::Backend::Type::OpenGL> {
static constexpr const char* name = "ColorReliefShader";
static constexpr const char* vertex = R"(layout(std140) uniform ColorReliefDrawableUBO {
highp mat4 u_matrix;
};
layout(std140) uniform ColorReliefTilePropsUBO {
highp vec4 u_unpack;
highp vec2 u_dimension;
highp int u_color_ramp_size;
highp float pad_tile0;
};
layout (location = 0) in vec2 a_pos;
layout (location = 1) in vec2 a_texture_pos;
out vec2 v_pos;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
highp vec2 epsilon = 1.0 / u_dimension;
float scale = (u_dimension.x - 2.0) / u_dimension.x;
v_pos = (a_texture_pos / 8192.0) * scale + epsilon;
// Handle poles
if (a_pos.y < -32767.5) v_pos.y = 0.0;
if (a_pos.y > 32766.5) v_pos.y = 1.0;
}
)";
static constexpr const char* fragment = R"(layout(std140) uniform ColorReliefDrawableUBO {
highp mat4 u_matrix;
};
layout(std140) uniform ColorReliefTilePropsUBO {
highp vec4 u_unpack;
highp vec2 u_dimension;
highp int u_color_ramp_size;
highp float pad_tile0;
};
layout(std140) uniform ColorReliefEvaluatedPropsUBO {
highp float u_opacity;
highp float pad_eval0;
highp float pad_eval1;
highp float pad_eval2;
};
uniform sampler2D u_image;
uniform sampler2D u_elevation_stops;
uniform sampler2D u_color_stops;
in vec2 v_pos;
float getElevation(vec2 coord) {
// Convert encoded elevation value to meters
vec4 data = texture(u_image, coord) * 255.0;
data.a = -1.0;
return dot(data, u_unpack);
}
float getElevationStop(int stop) {
// Elevation stops are plain float values, not terrain-RGB encoded
float x = (float(stop) + 0.5) / float(u_color_ramp_size);
return texture(u_elevation_stops, vec2(x, 0.5)).r;
}
vec4 getColorStop(int stop) {
float x = (float(stop) + 0.5) / float(u_color_ramp_size);
return texture(u_color_stops, vec2(x, 0.5));
}
void main() {
float el = getElevation(v_pos);
// Binary search for color stops
int r = (u_color_ramp_size - 1);
int l = 0;
while (r - l > 1) {
int m = (r + l) / 2;
float el_m = getElevationStop(m);
if (el < el_m) {
r = m;
} else {
l = m;
}
}
// Get elevation values for interpolation
float el_l = getElevationStop(l);
float el_r = getElevationStop(r);
// Get colors for interpolation
vec4 color_l = getColorStop(l);
vec4 color_r = getColorStop(r);
// Interpolate between the two colors
// Guard against division by zero when el_r == el_l
float denom = el_r - el_l;
float t = (abs(denom) < 0.0001) ? 0.0 : clamp((el - el_l) / denom, 0.0, 1.0);
fragColor = u_opacity * mix(color_l, color_r, t);
#ifdef OVERDRAW_INSPECTOR
fragColor = vec4(1.0);
#endif
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -69,7 +69,7 @@ mediump float radius = u_radius;
// weight * u_intensity * GAUSS_COEF * exp(-0.5 * 3.0^2 * S^2) == ZERO
// Which solves to:
// S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0
float S = sqrt(-2.0 * log(ZERO / weight / u_intensity / GAUSS_COEF)) / 3.0;
float S = sqrt(-2.0 * log(ZERO / (max(weight, ZERO) * max(u_intensity, ZERO) * GAUSS_COEF))) / 3.0;
// Pass the varying in units of radius
v_extrude = S * unscaled_extrude;

View File

@@ -27,53 +27,183 @@ uniform sampler2D u_image;
layout(std140) uniform HillshadeTilePropsUBO {
highp vec2 u_latrange;
highp vec2 u_light;
highp float u_exaggeration;
highp int u_method; // Hillshade method (0: STANDARD, 1: COMBINED, 2: IGOR, 3: MULTIDIRECTIONAL, 4: BASIC)
highp int u_num_lights; // Number of light sources (1-4)
highp float u_pad0;
highp float u_pad1;
highp float u_pad2;
};
layout(std140) uniform HillshadeEvaluatedPropsUBO {
highp vec4 u_highlight;
highp vec4 u_shadow;
highp vec4 u_accent;
highp vec4 u_altitudes; // Up to 4 altitude values (in radians)
highp vec4 u_azimuths; // Up to 4 azimuth values (in radians)
highp vec4 u_shadows[4]; // Shadow colors (up to 4 lights)
highp vec4 u_highlights[4]; // Highlight colors (up to 4 lights)
};
#define PI 3.141592653589793
#define STANDARD 0
#define COMBINED 1
#define IGOR 2
#define MULTIDIRECTIONAL 3
#define BASIC 4
float get_aspect(vec2 deriv) {
return deriv.x != 0.0 ?
atan(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);
}
// MapLibre's legacy hillshade algorithm (Method 0)
void standard_hillshade(vec2 deriv) {
float azimuth = u_azimuths.x + PI;
float slope = atan(0.625 * length(deriv));
float aspect = get_aspect(deriv);
// Note: This implementation uses u_exaggeration as intensity, though it typically should be derived from u_altitudes.x.
float intensity = u_exaggeration;
// Scale the slope exponentially based on intensity
float base = 1.875 - intensity * 1.75;
float maxValue = 0.5 * PI;
float scaledSlope = abs(intensity - 0.5) > 1e-6 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;
float accent = cos(scaledSlope);
vec4 accent_color = (1.0 - accent) * u_accent * clamp(intensity * 2.0, 0.0, 1.0);
// Calculate shadow/highlight based on aspect
float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
vec4 shade_color = mix(u_shadows[0], u_highlights[0], shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
fragColor = accent_color * (1.0 - shade_color.a) + shade_color;
}
// Basic directional hillshade (Method 4)
void basic_hillshade(vec2 deriv) {
deriv = deriv * u_exaggeration * 2.0; // Exaggerate the slope derivative
float azimuth = u_azimuths.x + PI;
float cos_az = cos(azimuth);
float sin_az = sin(azimuth);
float cos_alt = cos(u_altitudes.x);
float sin_alt = sin(u_altitudes.x);
// Calculate the cosine of the angle between the light vector and the surface normal
float cang = (sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(deriv, deriv));
float shade = clamp(cang, 0.0, 1.0); // cang is the hillshade intensity [0, 1]
// Blend shadow and highlight based on intensity
if (shade > 0.5) {
fragColor = u_highlights[0] * (2.0 * shade - 1.0); // Highlight strength [0, 1]
} else {
fragColor = u_shadows[0] * (1.0 - 2.0 * shade); // Shadow strength [0, 1]
}
}
// Multidirectional hillshade (Method 3)
void multidirectional_hillshade(vec2 deriv) {
deriv = deriv * u_exaggeration * 2.0; // Exaggerate the slope derivative
fragColor = vec4(0, 0, 0, 0);
// Iterate through all light sources (up to u_num_lights, max 4)
for (int i = 0; i < 4; i++) {
if (i >= u_num_lights) break;
// Access altitude and azimuth from vec4 UBOs
float altitude = (i == 0) ? u_altitudes.x : (i == 1) ? u_altitudes.y : (i == 2) ? u_altitudes.z : u_altitudes.w;
float azimuth = (i == 0) ? u_azimuths.x : (i == 1) ? u_azimuths.y : (i == 2) ? u_azimuths.z : u_azimuths.w;
float cos_alt = cos(altitude);
float sin_alt = sin(altitude);
// Negate cos/sin azimuth for correct light direction in the normal calculation
float cos_az = -cos(azimuth);
float sin_az = -sin(azimuth);
// Calculate the cosine of the angle between the light vector and the surface normal
float cang = (sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) /
sqrt(1.0 + dot(deriv, deriv));
float shade = clamp(cang, 0.0, 1.0); // cang is the hillshade intensity [0, 1]
// Accumulate shadow/highlight contribution from each light
if (shade > 0.5) {
fragColor += u_highlights[i] * (2.0 * shade - 1.0) / float(u_num_lights);
} else {
fragColor += u_shadows[i] * (1.0 - 2.0 * shade) / float(u_num_lights);
}
}
}
// Combined shadow and highlight method (Method 1)
void combined_hillshade(vec2 deriv) {
// Only supports one light source (index 0)
deriv = deriv * u_exaggeration * 2.0;
float azimuth = u_azimuths.x + PI;
float cos_az = cos(azimuth);
float sin_az = sin(azimuth);
float cos_alt = cos(u_altitudes.x);
float sin_alt = sin(u_altitudes.x);
// Calculate the angle between the light vector and the surface normal
float cang = acos((sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) /
sqrt(1.0 + dot(deriv, deriv)));
cang = clamp(cang, 0.0, PI / 2.0); // Clamp angle to 90 degrees (half hemisphere)
// The "shade" and "highlight" components are calculated from cang (angle) and the magnitude of the slope
float shade = cang * atan(length(deriv)) * 4.0 / PI / PI;
float highlight = (PI / 2.0 - cang) * atan(length(deriv)) * 4.0 / PI / PI;
fragColor = u_shadows[0] * shade + u_highlights[0] * highlight;
}
// Igor's shadow/highlight method (Method 2)
void igor_hillshade(vec2 deriv) {
// Only supports one light source (index 0)
deriv = deriv * u_exaggeration * 2.0;
float aspect = get_aspect(deriv);
float azimuth = u_azimuths.x + PI;
// Slope strength is magnitude of slope vector, normalized to [0, 1]
float slope_strength = atan(length(deriv)) * 2.0 / PI;
// Aspect strength is difference between aspect and light azimuth, normalized to [0, 1]
float aspect_strength = 1.0 - abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
float shadow_strength = slope_strength * aspect_strength;
float highlight_strength = slope_strength * (1.0 - aspect_strength);
fragColor = u_shadows[0] * shadow_strength + u_highlights[0] * highlight_strength;
}
void main() {
vec4 pixel = texture(u_image, v_pos);
vec2 deriv = ((pixel.rg * 2.0) - 1.0);
// We divide the slope by a scale factor based on the cosin of the pixel's approximate latitude
// to account for mercator projection distortion. see #4807 for details
// Scale the derivative based on the mercator distortion at this latitude (v_pos.y)
float scaleFactor = cos(radians((u_latrange[0] - u_latrange[1]) * (1.0 - v_pos.y) + u_latrange[1]));
// We also multiply the slope by an arbitrary z-factor of 1.25
float slope = atan(1.25 * length(deriv) / scaleFactor);
float aspect = deriv.x != 0.0 ? atan(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);
float intensity = u_light.x;
// We add PI to make this property match the global light object, which adds PI/2 to the light's azimuthal
// position property to account for 0deg corresponding to north/the top of the viewport in the style spec
// and the original shader was written to accept (-illuminationDirection - 90) as the azimuthal.
float azimuth = u_light.y + PI;
// The derivative is scaled back from [0, 1] texture range to world-space slope
// Texture range [0, 1] corresponds to slope range [-4, 4] (8.0 * 0.5 * deriv)
vec2 deriv = ((pixel.rg * 8.0) - 4.0) / scaleFactor;
// We scale the slope exponentially based on intensity, using a calculation similar to
// the exponential interpolation function in the style spec:
// https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/expression/definitions/interpolate.js#L217-L228
// so that higher intensity values create more opaque hillshading.
float base = 1.875 - intensity * 1.75;
float maxValue = 0.5 * PI;
float scaledSlope = intensity != 0.5 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;
// The accent color is calculated with the cosine of the slope while the shade color is calculated with the sine
// so that the accent color's rate of change eases in while the shade color's eases out.
float accent = cos(scaledSlope);
// We multiply both the accent and shade color by a clamped intensity value
// so that intensities >= 0.5 do not additionally affect the color values
// while intensity values < 0.5 make the overall color more transparent.
vec4 accent_color = (1.0 - accent) * u_accent * clamp(intensity * 2.0, 0.0, 1.0);
float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
vec4 shade_color = mix(u_shadow, u_highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
fragColor = accent_color * (1.0 - shade_color.a) + shade_color;
// Dispatch to the selected hillshade method
switch (u_method) {
case BASIC:
basic_hillshade(deriv);
break;
case COMBINED:
combined_hillshade(deriv);
break;
case IGOR:
igor_hillshade(deriv);
break;
case MULTIDIRECTIONAL:
multidirectional_hillshade(deriv);
break;
case STANDARD:
default:
standard_hillshade(deriv);
break;
}
#ifdef OVERDRAW_INSPECTOR
fragColor = vec4(1.0);

View File

@@ -38,7 +38,6 @@ precision highp float;
in vec2 v_pos;
uniform sampler2D u_image;
layout (std140) uniform HillshadePrepareTilePropsUBO {
highp vec4 u_unpack;
highp vec2 u_dimension;
@@ -50,13 +49,14 @@ float getElevation(vec2 coord, float bias) {
// Convert encoded elevation value to meters
vec4 data = texture(u_image, coord) * 255.0;
data.a = -1.0;
return dot(data, u_unpack) / 4.0;
return dot(data, u_unpack);
}
void main() {
vec2 epsilon = 1.0 / u_dimension;
float tileSize = u_dimension.x - 2.0;
// queried pixels:
// queried pixels (using Sobel operator kernel):
// +-----------+
// | | | |
// | a | b | c |
@@ -81,30 +81,28 @@ void main() {
float h = getElevation(v_pos + vec2(0, epsilon.y), 0.0);
float i = getElevation(v_pos + vec2(epsilon.x, epsilon.y), 0.0);
// here we divide the x and y slopes by 8 * pixel size
// where pixel size (aka meters/pixel) is:
// circumference of the world / (pixels per tile * number of tiles)
// which is equivalent to: 8 * 40075016.6855785 / (512 * pow(2, u_zoom))
// which can be reduced to: pow(2, 19.25619978527 - u_zoom)
// we want to vertically exaggerate the hillshading though, because otherwise
// it is barely noticeable at low zooms. to do this, we multiply this by some
// scale factor pow(2, (u_zoom - u_maxzoom) * a) where a is an arbitrary value
// Here we use a=0.3 which works out to the expression below. see
// nickidlugash's awesome breakdown for more info
// Convert the raw pixel-space derivative (slope) into world-space slope.
// The conversion factor is: tileSize / (8 * meters_per_pixel).
// meters_per_pixel is calculated as pow(2.0, 28.2562 - u_zoom).
// The exaggeration factor is applied to scale the effect at lower zooms.
// See nickidlugash's awesome breakdown for more info
// https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556
float exaggeration = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;
float exaggerationFactor = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;
float exaggeration = u_zoom < 15.0 ? (u_zoom - 15.0) * exaggerationFactor : 0.0;
vec2 deriv = vec2(
(c + f + f + i) - (a + d + d + g),
(g + h + h + i) - (a + b + b + c)
) / pow(2.0, (u_zoom - u_maxzoom) * exaggeration + 19.2562 - u_zoom);
) * tileSize / pow(2.0, exaggeration + (28.2562 - u_zoom));
// Encode the derivative into the color channels (r and g)
// The derivative is scaled from world-space slope to the range [0, 1] for texture storage.
// The maximum possible world-space derivative is assumed to be 4 (hence division by 8.0).
fragColor = clamp(vec4(
deriv.x / 2.0 + 0.5,
deriv.y / 2.0 + 0.5,
deriv.x / 8.0 + 0.5,
deriv.y / 8.0 + 0.5,
1.0,
1.0), 0.0, 1.0);
#ifdef OVERDRAW_INSPECTOR
fragColor = vec4(1.0);
#endif

View File

@@ -163,6 +163,13 @@ struct ShaderInfo<BuiltIn::HillshadeShader, gfx::Backend::Type::OpenGL> {
static const std::vector<TextureInfo> textures;
};
template <>
struct ShaderInfo<BuiltIn::ColorReliefShader, gfx::Backend::Type::OpenGL> {
static const std::vector<AttributeInfo> attributes;
static const std::vector<UniformBlockInfo> uniformBlocks;
static const std::vector<TextureInfo> textures;
};
template <>
struct ShaderInfo<BuiltIn::LineGradientShader, gfx::Backend::Type::OpenGL> {
static const std::vector<AttributeInfo> attributes;

View File

@@ -1,10 +1,20 @@
#pragma once
#include <mbgl/shaders/layer_ubo.hpp>
namespace mbgl {
namespace shaders {
// Maximum number of illumination sources supported
constexpr int MAX_ILLUMINATION_SOURCES = 4;
enum class HillshadeMethod : int32_t {
Standard = 0,
Combined = 1,
Igor = 2,
Multidirectional = 3,
Basic = 4
};
struct alignas(16) HillshadeDrawableUBO {
/* 0 */ std::array<float, 4 * 4> matrix;
/* 64 */
@@ -13,19 +23,26 @@ static_assert(sizeof(HillshadeDrawableUBO) == 4 * 16);
struct alignas(16) HillshadeTilePropsUBO {
/* 0 */ std::array<float, 2> latrange;
/* 8 */ std::array<float, 2> light;
/* 16 */
/* 8 */ float exaggeration; // NEW: replaces light[0] (intensity)
/* 12 */ int32_t method; // NEW: hillshade method (0-4)
/* 16 */ int32_t num_lights; // NEW: number of light sources (1-4)
/* 20 */ float pad0; // Padding for alignment
/* 24 */ float pad1; // Padding for alignment
/* 28 */ float pad2; // Padding for alignment
/* 32 */
};
static_assert(sizeof(HillshadeTilePropsUBO) == 16);
static_assert(sizeof(HillshadeTilePropsUBO) == 32);
/// Evaluated properties that do not depend on the tile
struct alignas(16) HillshadeEvaluatedPropsUBO {
/* 0 */ Color highlight;
/* 16 */ Color shadow;
/* 32 */ Color accent;
/* 48 */
/* 0 */ Color accent;
/* 16 */ std::array<float, 4> altitudes; // NEW: altitude values in radians (up to 4)
/* 32 */ std::array<float, 4> azimuths; // NEW: azimuth values in radians (up to 4)
/* 48 */ std::array<float, 16> shadows; // NEW: 4 shadow colors (4 colors × 4 RGBA)
/* 112 */ std::array<float, 16> highlights; // NEW: 4 highlight colors (4 colors × 4 RGBA)
/* 176 */
};
static_assert(sizeof(HillshadeEvaluatedPropsUBO) == 3 * 16);
static_assert(sizeof(HillshadeEvaluatedPropsUBO) == 176);
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,170 @@
#pragma once
#include <mbgl/shaders/color_relief_layer_ubo.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
namespace mbgl {
namespace shaders {
constexpr auto colorReliefShaderPrelude = R"(
enum {
idColorReliefDrawableUBO = idDrawableReservedVertexOnlyUBO,
idColorReliefTilePropsUBO = drawableReservedUBOCount,
idColorReliefEvaluatedPropsUBO,
colorReliefUBOCount
};
struct alignas(16) ColorReliefDrawableUBO {
/* 0 */ float4x4 matrix;
/* 64 */
};
static_assert(sizeof(ColorReliefDrawableUBO) == 4 * 16, "wrong size");
struct alignas(16) ColorReliefTilePropsUBO {
/* 0 */ float4 unpack;
/* 16 */ float2 dimension;
/* 24 */ int32_t color_ramp_size;
/* 28 */ float pad_tile0;
/* 32 */
};
static_assert(sizeof(ColorReliefTilePropsUBO) == 2 * 16, "wrong size");
struct alignas(16) ColorReliefEvaluatedPropsUBO {
/* 0 */ float opacity;
/* 4 */ float pad_eval0;
/* 8 */ float pad_eval1;
/* 12 */ float pad_eval2;
/* 16 */
};
static_assert(sizeof(ColorReliefEvaluatedPropsUBO) == 16, "wrong size");
)";
template <>
struct ShaderSource<BuiltIn::ColorReliefShader, gfx::Backend::Type::Metal> {
static constexpr auto name = "ColorReliefShader";
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 3> textures;
static constexpr auto prelude = colorReliefShaderPrelude;
static constexpr auto source = R"(
struct VertexStage {
short2 pos [[attribute(colorReliefUBOCount + 0)]];
short2 texture_pos [[attribute(colorReliefUBOCount + 1)]];
};
struct FragmentStage {
float4 position [[position, invariant]];
float2 pos;
};
FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
device const uint32_t& uboIndex [[buffer(idGlobalUBOIndex)]],
device const ColorReliefDrawableUBO* drawableVector [[buffer(idColorReliefDrawableUBO)]],
device const ColorReliefTilePropsUBO* tilePropsVector [[buffer(idColorReliefTilePropsUBO)]]) {
device const ColorReliefDrawableUBO& drawable = drawableVector[uboIndex];
device const ColorReliefTilePropsUBO& tileProps = tilePropsVector[uboIndex];
const float4 position = drawable.matrix * float4(float2(vertx.pos), 0, 1);
// Calculate texture coordinate
float2 epsilon = 1.0 / tileProps.dimension;
float scale = (tileProps.dimension.x - 2.0) / tileProps.dimension.x;
float2 pos = (float2(vertx.pos) / 8192.0) * scale + epsilon;
// Handle poles (use vertx.pos, not texture_pos, to match GLSL a_pos)
if (float(vertx.pos.y) < -32767.5) pos.y = 0.0;
if (float(vertx.pos.y) > 32766.5) pos.y = 1.0;
return {
.position = position,
.pos = pos,
};
}
// Function to convert terrain-RGB encoded elevation value to meters
float getElevation(float2 coord, texture2d<float, access::sample> image, sampler image_sampler, float4 unpack) {
float4 data = image.sample(image_sampler, coord) * 255.0;
data.a = -1.0;
return dot(data, unpack);
}
// Function to get the elevation value at a specific color ramp stop
float getElevationStop(int stop, int color_ramp_size, texture2d<float, access::sample> elevationStops, sampler elevation_sampler) {
// Elevation stops are plain float values, stored in the R channel
float x = (float(stop) + 0.5) / float(color_ramp_size);
return elevationStops.sample(elevation_sampler, float2(x, 0.5)).r;
}
// Function to get the color value at a specific color ramp stop
float4 getColorStop(int stop, int color_ramp_size, texture2d<float, access::sample> colorStops, sampler color_sampler) {
float x = (float(stop) + 0.5) / float(color_ramp_size);
return colorStops.sample(color_sampler, float2(x, 0.5));
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
device const uint32_t& uboIndex [[buffer(idGlobalUBOIndex)]],
device const ColorReliefTilePropsUBO* tilePropsVector [[buffer(idColorReliefTilePropsUBO)]],
device const ColorReliefEvaluatedPropsUBO& props [[buffer(idColorReliefEvaluatedPropsUBO)]],
texture2d<float, access::sample> image [[texture(0)]],
texture2d<float, access::sample> elevationStops [[texture(1)]],
texture2d<float, access::sample> colorStops [[texture(2)]],
sampler image_sampler [[sampler(0)]],
sampler elevation_stops_sampler [[sampler(1)]],
sampler color_stops_sampler [[sampler(2)]]) {
#if defined(OVERDRAW_INSPECTOR)
return half4(1.0);
#endif
device const ColorReliefTilePropsUBO& tileProps = tilePropsVector[uboIndex];
// 1. Get elevation at this pixel from DEM
float el = getElevation(in.pos, image, image_sampler, tileProps.unpack);
// 2. Binary search to find surrounding elevation stops (l and r indices)
int r = tileProps.color_ramp_size - 1;
int l = 0;
// Perform binary search
while (r - l > 1) {
int m = (r + l) / 2;
float el_m = getElevationStop(m, tileProps.color_ramp_size, elevationStops, elevation_stops_sampler);
if (el < el_m) {
r = m;
} else {
l = m;
}
}
// 3. Get the elevation values and colors at the stops
float el_l = getElevationStop(l, tileProps.color_ramp_size, elevationStops, elevation_stops_sampler);
float el_r = getElevationStop(r, tileProps.color_ramp_size, elevationStops, elevation_stops_sampler);
float4 color_l = getColorStop(l, tileProps.color_ramp_size, colorStops, color_stops_sampler);
float4 color_r = getColorStop(r, tileProps.color_ramp_size, colorStops, color_stops_sampler);
// 4. Interpolate color based on elevation
// Guard against division by zero when el_r == el_l
float denom = el_r - el_l;
float t = (abs(denom) < 0.0001) ? 0.0 : clamp((el - el_l) / denom, 0.0, 1.0);
thread half4 fragColor;
float4 color = mix(color_l, color_r, t);
color *= props.opacity;
fragColor = half4(color);
return fragColor;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -23,7 +23,7 @@ using namespace metal;
template <typename T1, typename T2>
inline auto glMod(T1 x, T2 y) { return x - y * metal::floor(x/y); }
float radians(float degrees) {
inline float radians(float degrees) {
return M_PI_F * degrees / 180.0;
}

View File

@@ -109,7 +109,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
// weight * u_intensity * GAUSS_COEF * exp(-0.5 * 3.0^2 * S^2) == ZERO
// Which solves to:
// S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0
const float S = sqrt(-2.0 * log(ZERO / weight / props.intensity / GAUSS_COEF)) / 3.0;
const float S = sqrt(-2.0 * log(ZERO / (max(weight, ZERO) * max(props.intensity, ZERO) * GAUSS_COEF))) / 3.0;
// Pass the varying in units of radius
const float2 extrude = S * unscaled_extrude;

View File

@@ -24,19 +24,26 @@ static_assert(sizeof(HillshadeDrawableUBO) == 4 * 16, "wrong size");
struct alignas(16) HillshadeTilePropsUBO {
/* 0 */ float2 latrange;
/* 8 */ float2 light;
/* 16 */
/* 8 */ float exaggeration;
/* 12 */ int32_t method;
/* 16 */ int32_t num_lights;
/* 20 */ float pad0;
/* 24 */ float pad1;
/* 28 */ float pad2;
/* 32 */
};
static_assert(sizeof(HillshadeTilePropsUBO) == 16, "wrong size");
static_assert(sizeof(HillshadeTilePropsUBO) == 2 * 16, "wrong size");
/// Evaluated properties that do not depend on the tile
struct alignas(16) HillshadeEvaluatedPropsUBO {
/* 0 */ float4 highlight;
/* 16 */ float4 shadow;
/* 32 */ float4 accent;
/* 48 */
/* 0 */ float4 accent;
/* 16 */ float4 altitudes; // Up to 4 altitude values (in radians)
/* 32 */ float4 azimuths; // Up to 4 azimuth values (in radians)
/* 48 */ float4 shadows[4]; // Shadow colors (up to 4 lights)
/* 112 */ float4 highlights[4]; // Highlight colors (up to 4 lights)
/* 176 */
};
static_assert(sizeof(HillshadeEvaluatedPropsUBO) == 3 * 16, "wrong size");
static_assert(sizeof(HillshadeEvaluatedPropsUBO) == 11 * 16, "wrong size");
)";
@@ -53,6 +60,13 @@ struct ShaderSource<BuiltIn::HillshadeShader, gfx::Backend::Type::Metal> {
static constexpr auto prelude = hillshadeShaderPrelude;
static constexpr auto source = R"(
#define PI 3.141592653589793
#define STANDARD 0
#define COMBINED 1
#define IGOR 2
#define MULTIDIRECTIONAL 3
#define BASIC 4
struct VertexStage {
short2 pos [[attribute(hillshadeUBOCount + 0)]];
short2 texture_pos [[attribute(hillshadeUBOCount + 1)]];
@@ -71,6 +85,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const float4 position = drawable.matrix * float4(float2(vertx.pos), 0, 1);
float2 pos = float2(vertx.texture_pos) / 8192.0;
// Metal's texture coordinate origin differs from some renderers;
// restore Y-flip to match prepare pass and historical behavior.
pos.y = 1.0 - pos.y;
return {
@@ -79,6 +95,129 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
};
}
// Helper function to calculate aspect (normalized direction of slope)
float get_aspect(float2 deriv) {
return deriv.x != 0.0 ? atan2(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);
}
// MapLibre's legacy hillshade algorithm (Method 0: STANDARD)
void standard_hillshade(float2 deriv, device const HillshadeTilePropsUBO& tileProps, device const HillshadeEvaluatedPropsUBO& props, thread half4& fragColor) {
float azimuth = props.azimuths.x + PI;
float slope = atan(0.625 * length(deriv));
float aspect = get_aspect(deriv);
float intensity = tileProps.exaggeration;
// Scale the slope exponentially based on intensity
float base = 1.875 - intensity * 1.75;
float maxValue = 0.5 * PI;
float scaledSlope = abs(intensity - 0.5) > 1e-6 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;
float accent = cos(scaledSlope);
float4 accent_color = (1.0 - accent) * props.accent * clamp(intensity * 2.0, 0.0, 1.0);
float shade = abs(glMod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
float4 shade_color = mix(props.shadows[0], props.highlights[0], shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
fragColor = half4(accent_color * (1.0 - shade_color.a) + shade_color);
}
// Basic directional hillshade (Method 4: BASIC)
void basic_hillshade(float2 deriv, device const HillshadeTilePropsUBO& tileProps, device const HillshadeEvaluatedPropsUBO& props, thread half4& fragColor) {
deriv = deriv * tileProps.exaggeration * 2.0;
float azimuth = props.azimuths.x + PI;
float cos_az = cos(azimuth);
float sin_az = sin(azimuth);
float cos_alt = cos(props.altitudes.x);
float sin_alt = sin(props.altitudes.x);
// Calculate the cosine of the angle between the light vector and the surface normal
float cang = (sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(deriv, deriv));
float shade = clamp(cang, 0.0, 1.0);
// Blend shadow and highlight based on intensity
if (shade > 0.5) {
fragColor = half4(props.highlights[0] * (2.0 * shade - 1.0));
} else {
fragColor = half4(props.shadows[0] * (1.0 - 2.0 * shade));
}
}
// Multidirectional hillshade (Method 3: MULTIDIRECTIONAL)
void multidirectional_hillshade(float2 deriv, device const HillshadeTilePropsUBO& tileProps, device const HillshadeEvaluatedPropsUBO& props, thread half4& fragColor) {
deriv = deriv * tileProps.exaggeration * 2.0;
float4 total_color = float4(0, 0, 0, 0);
int num_lights = min(tileProps.num_lights, 4);
// Iterate through all light sources
for (int i = 0; i < num_lights; i++) {
// Access altitude and azimuth from vec4 UBOs
float altitude = (i == 0) ? props.altitudes.x : (i == 1) ? props.altitudes.y : (i == 2) ? props.altitudes.z : props.altitudes.w;
float azimuth = (i == 0) ? props.azimuths.x : (i == 1) ? props.azimuths.y : (i == 2) ? props.azimuths.z : props.azimuths.w;
float cos_alt = cos(altitude);
float sin_alt = sin(altitude);
// Negate cos/sin azimuth for correct light direction
float cos_az = -cos(azimuth);
float sin_az = -sin(azimuth);
// Calculate the cosine of the angle between the light vector and the surface normal
float cang = (sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(deriv, deriv));
float shade = clamp(cang, 0.0, 1.0);
// Accumulate shadow/highlight contribution from each light
if (shade > 0.5) {
total_color += props.highlights[i] * (2.0 * shade - 1.0) / float(num_lights);
} else {
total_color += props.shadows[i] * (1.0 - 2.0 * shade) / float(num_lights);
}
}
fragColor = half4(total_color);
}
// Combined shadow and highlight method (Method 1: COMBINED)
void combined_hillshade(float2 deriv, device const HillshadeTilePropsUBO& tileProps, device const HillshadeEvaluatedPropsUBO& props, thread half4& fragColor) {
// Only supports one light source (index 0)
deriv = deriv * tileProps.exaggeration * 2.0;
float azimuth = props.azimuths.x + PI;
float cos_az = cos(azimuth);
float sin_az = sin(azimuth);
float cos_alt = cos(props.altitudes.x);
float sin_alt = sin(props.altitudes.x);
// Calculate the angle between the light vector and the surface normal
float cang = acos((sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(deriv, deriv)));
cang = clamp(cang, 0.0, PI / 2.0);
// Calculate shade and highlight components from angle and slope magnitude
float shade = cang * atan(length(deriv)) * 4.0 / PI / PI;
float highlight = (PI / 2.0 - cang) * atan(length(deriv)) * 4.0 / PI / PI;
fragColor = half4(props.shadows[0] * shade + props.highlights[0] * highlight);
}
// Igor's shadow/highlight method (Method 2: IGOR)
void igor_hillshade(float2 deriv, device const HillshadeTilePropsUBO& tileProps, device const HillshadeEvaluatedPropsUBO& props, thread half4& fragColor) {
// Only supports one light source (index 0)
deriv = deriv * tileProps.exaggeration * 2.0;
float aspect = get_aspect(deriv);
float azimuth = props.azimuths.x + PI;
// Slope strength is magnitude of slope vector, normalized to [0, 1]
float slope_strength = atan(length(deriv)) * 2.0 / PI;
// Aspect strength is difference between aspect and light azimuth, normalized to [0, 1]
float aspect_strength = 1.0 - abs(glMod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
float shadow_strength = slope_strength * aspect_strength;
float highlight_strength = slope_strength * (1.0 - aspect_strength);
fragColor = half4(props.shadows[0] * shadow_strength + props.highlights[0] * highlight_strength);
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
device const uint32_t& uboIndex [[buffer(idGlobalUBOIndex)]],
device const HillshadeTilePropsUBO* tilePropsVector [[buffer(idHillshadeTilePropsUBO)]],
@@ -90,44 +229,33 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
#endif
device const HillshadeTilePropsUBO& tileProps = tilePropsVector[uboIndex];
thread half4 fragColor;
float4 pixel = image.sample(image_sampler, in.pos);
float2 deriv = ((pixel.rg * 2.0) - 1.0);
// Scale the derivative based on the mercator distortion at this latitude
float latitude = (tileProps.latrange.x - tileProps.latrange.y) * in.pos.y + tileProps.latrange.y;
float scaleFactor = cos(radians(latitude));
// We divide the slope by a scale factor based on the cosin of the pixel's approximate latitude
// to account for mercator projection distortion. see #4807 for details
float scaleFactor = cos(radians((tileProps.latrange[0] - tileProps.latrange[1]) * in.pos.y + tileProps.latrange[1]));
// We also multiply the slope by an arbitrary z-factor of 1.25
float slope = atan(1.25 * length(deriv) / scaleFactor);
float aspect = deriv.x != 0.0 ? atan2(deriv.y, -deriv.x) : M_PI_F / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);
// The derivative is scaled back from [0, 1] texture range to world-space slope
// Texture range [0, 1] corresponds to slope range [-4, 4]
float2 deriv = ((pixel.rg * 8.0) - 4.0) / scaleFactor;
float intensity = tileProps.light.x;
// We add PI to make this property match the global light object, which adds PI/2 to the light's azimuthal
// position property to account for 0deg corresponding to north/the top of the viewport in the style spec
// and the original shader was written to accept (-illuminationDirection - 90) as the azimuthal.
float azimuth = tileProps.light.y + M_PI_F;
// Dispatch to the selected hillshade method
if (tileProps.method == BASIC) {
basic_hillshade(deriv, tileProps, props, fragColor);
} else if (tileProps.method == COMBINED) {
combined_hillshade(deriv, tileProps, props, fragColor);
} else if (tileProps.method == IGOR) {
igor_hillshade(deriv, tileProps, props, fragColor);
} else if (tileProps.method == MULTIDIRECTIONAL) {
multidirectional_hillshade(deriv, tileProps, props, fragColor);
} else {
// Default to STANDARD
standard_hillshade(deriv, tileProps, props, fragColor);
}
// We scale the slope exponentially based on intensity, using a calculation similar to
// the exponential interpolation function in the style spec:
// https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/expression/definitions/interpolate.js#L217-L228
// so that higher intensity values create more opaque hillshading.
float base = 1.875 - intensity * 1.75;
float maxValue = 0.5 * M_PI_F;
float scaledSlope = intensity != 0.5 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;
// The accent color is calculated with the cosine of the slope while the shade color is calculated with the sine
// so that the accent color's rate of change eases in while the shade color's eases out.
float accent = cos(scaledSlope);
// We multiply both the accent and shade color by a clamped intensity value
// so that intensities >= 0.5 do not additionally affect the color values
// while intensity values < 0.5 make the overall color more transparent.
float4 accent_color = (1.0 - accent) * props.accent * clamp(intensity * 2.0, 0.0, 1.0);
float shade = abs(glMod((aspect + azimuth) / M_PI_F + 0.5, 2.0) - 1.0);
float4 shade_color = mix(props.shadow, props.highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
float4 color = accent_color * (1.0 - shade_color.a) + shade_color;
return half4(color);
return fragColor;
}
)";
};

View File

@@ -63,7 +63,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
float2 epsilon = 1.0 / tileProps.dimension;
float scale = (tileProps.dimension.x - 2.0) / tileProps.dimension.x;
const float2 pos = (float2(vertx.texture_pos) / 8192.0) * scale + epsilon;
float2 pos = (float2(vertx.texture_pos) / 8192.0) * scale + epsilon;
return {
.position = position,
@@ -75,7 +75,7 @@ float getElevation(float2 coord, float bias, texture2d<float, access::sample> im
// Convert encoded elevation value to meters
float4 data = image.sample(image_sampler, coord) * 255.0;
data.a = -1.0;
return dot(data, unpack) / 4.0;
return dot(data, unpack);
}
half4 fragment fragmentMain(FragmentStage in [[stage_in]],
@@ -87,8 +87,9 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
#endif
float2 epsilon = 1.0 / tileProps.dimension;
float tileSize = tileProps.dimension.x - 2.0;
// queried pixels:
// queried pixels (using Sobel operator kernel):
// +-----------+
// | | | |
// | a | b | c |
@@ -102,7 +103,6 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
// | g | h | i |
// | | | |
// +-----------+
float a = getElevation(in.pos + float2(-epsilon.x, -epsilon.y), 0.0, image, image_sampler, tileProps.unpack);
float b = getElevation(in.pos + float2(0, -epsilon.y), 0.0, image, image_sampler, tileProps.unpack);
float c = getElevation(in.pos + float2(epsilon.x, -epsilon.y), 0.0, image, image_sampler, tileProps.unpack);
@@ -113,27 +113,24 @@ half4 fragment fragmentMain(FragmentStage in [[stage_in]],
float h = getElevation(in.pos + float2(0, epsilon.y), 0.0, image, image_sampler, tileProps.unpack);
float i = getElevation(in.pos + float2(epsilon.x, epsilon.y), 0.0, image, image_sampler, tileProps.unpack);
// here we divide the x and y slopes by 8 * pixel size
// where pixel size (aka meters/pixel) is:
// circumference of the world / (pixels per tile * number of tiles)
// which is equivalent to: 8 * 40075016.6855785 / (512 * pow(2, u_zoom))
// which can be reduced to: pow(2, 19.25619978527 - u_zoom)
// we want to vertically exaggerate the hillshading though, because otherwise
// it is barely noticeable at low zooms. to do this, we multiply this by some
// scale factor pow(2, (u_zoom - u_maxzoom) * a) where a is an arbitrary value
// Here we use a=0.3 which works out to the expression below. see
// nickidlugash's awesome breakdown for more info
// https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556
float exaggeration = tileProps.zoom < 2.0 ? 0.4 : tileProps.zoom < 4.5 ? 0.35 : 0.3;
// Convert the raw pixel-space derivative (slope) into world-space slope.
// The conversion factor is: tileSize / (8 * meters_per_pixel).
// meters_per_pixel is calculated as pow(2.0, 28.2562 - u_zoom).
// The exaggeration factor is applied to scale the effect at lower zooms.
float exaggerationFactor = tileProps.zoom < 2.0 ? 0.4 : tileProps.zoom < 4.5 ? 0.35 : 0.3;
float exaggeration = tileProps.zoom < 15.0 ? (tileProps.zoom - 15.0) * exaggerationFactor : 0.0;
float2 deriv = float2(
(c + f + f + i) - (a + d + d + g),
(g + h + h + i) - (a + b + b + c)
) / pow(2.0, (tileProps.zoom - tileProps.maxzoom) * exaggeration + 19.2562 - tileProps.zoom);
) * tileSize / pow(2.0, exaggeration + (28.2562 - tileProps.zoom));
// Encode the derivative into the color channels (r and g)
// The derivative is scaled from world-space slope to the range [0, 1] for texture storage.
// The maximum possible world-space derivative is assumed to be 4 (hence division by 8.0).
float4 color = clamp(float4(
deriv.x / 2.0 + 0.5,
deriv.y / 2.0 + 0.5,
deriv.x / 8.0 + 0.5,
deriv.y / 8.0 + 0.5,
1.0,
1.0), 0.0, 1.0);

View File

@@ -78,6 +78,12 @@ enum {
hillshadePrepareDrawableUBOCount
};
enum {
idColorReliefDrawableUBO = idDrawableReservedVertexOnlyUBO,
idColorReliefTilePropsUBO = drawableReservedUBOCount,
colorReliefDrawableUBOCount
};
enum {
idLineDrawableUBO = idDrawableReservedVertexOnlyUBO,
idLineTilePropsUBO = idDrawableReservedFragmentOnlyUBO,
@@ -120,6 +126,7 @@ static constexpr uint32_t layerUBOStartId = std::max({static_cast<uint32_t>(back
static_cast<uint32_t>(heatmapTextureDrawableUBOCount),
static_cast<uint32_t>(hillshadeDrawableUBOCount),
static_cast<uint32_t>(hillshadePrepareDrawableUBOCount),
static_cast<uint32_t>(colorReliefDrawableUBOCount),
static_cast<uint32_t>(lineDrawableUBOCount),
static_cast<uint32_t>(locationIndicatorDrawableUBOCount),
static_cast<uint32_t>(rasterDrawableUBOCount),
@@ -195,6 +202,11 @@ enum {
hillshadePrepareUBOCount = getLayerStartValue(hillshadePrepareDrawableUBOCount)
};
enum {
idColorReliefEvaluatedPropsUBO = getLayerStartValue(colorReliefDrawableUBOCount),
colorReliefUBOCount
};
enum {
idLineEvaluatedPropsUBO = getLayerStartValue(lineDrawableUBOCount),
idLineExpressionUBO,
@@ -233,6 +245,7 @@ static constexpr uint32_t maxUBOCountPerShader = std::max({static_cast<uint32_t>
static_cast<uint32_t>(heatmapTextureUBOCount),
static_cast<uint32_t>(hillshadeUBOCount),
static_cast<uint32_t>(hillshadePrepareUBOCount),
static_cast<uint32_t>(colorReliefUBOCount),
static_cast<uint32_t>(lineUBOCount),
static_cast<uint32_t>(locationIndicatorUBOCount),
static_cast<uint32_t>(rasterUBOCount),
@@ -296,6 +309,13 @@ enum {
hillshadeTextureCount
};
enum {
idColorReliefImageTexture,
idColorReliefElevationStopsTexture,
idColorReliefColorStopsTexture,
colorReliefTextureCount
};
enum {
idLocationIndicatorTexture,
locationIndicatorTextureCount
@@ -329,6 +349,7 @@ static constexpr uint32_t maxTextureCountPerShader = std::max({static_cast<uint3
static_cast<uint32_t>(fillExtrusionTextureCount),
static_cast<uint32_t>(heatmapTextureCount),
static_cast<uint32_t>(hillshadeTextureCount),
static_cast<uint32_t>(colorReliefTextureCount),
static_cast<uint32_t>(lineTextureCount),
static_cast<uint32_t>(locationIndicatorTextureCount),
static_cast<uint32_t>(rasterTextureCount),
@@ -429,6 +450,12 @@ enum {
hillshadeVertexAttributeCount
};
enum {
idColorReliefPosVertexAttribute,
idColorReliefTexturePosVertexAttribute,
colorReliefVertexAttributeCount
};
enum {
idLinePosNormalVertexAttribute,
idLineDataVertexAttribute,
@@ -505,6 +532,7 @@ static constexpr uint32_t maxVertexAttributeCountPerShader = std::max({
static_cast<uint32_t>(fillExtrusionVertexAttributeCount),
static_cast<uint32_t>(heatmapVertexAttributeCount),
static_cast<uint32_t>(hillshadeVertexAttributeCount),
static_cast<uint32_t>(colorReliefVertexAttributeCount),
static_cast<uint32_t>(lineVertexAttributeCount),
static_cast<uint32_t>(locationIndicatorVertexAttributeCount),
static_cast<uint32_t>(rasterVertexAttributeCount),

View File

@@ -23,6 +23,7 @@
#include <mbgl/shaders/gl/heatmap.hpp>
#include <mbgl/shaders/gl/heatmap_texture.hpp>
#include <mbgl/shaders/gl/hillshade_prepare.hpp>
#include <mbgl/shaders/gl/color_relief.hpp>
#include <mbgl/shaders/gl/hillshade.hpp>
#include <mbgl/shaders/gl/line.hpp>
#include <mbgl/shaders/gl/line_gradient.hpp>

View File

@@ -29,6 +29,7 @@ enum class BuiltIn {
HeatmapShader,
HeatmapTextureShader,
HillshadePrepareShader,
ColorReliefShader,
HillshadeShader,
LineShader,
LineGradientShader,

View File

@@ -0,0 +1,167 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/vulkan/shader_program.hpp>
namespace mbgl {
namespace shaders {
constexpr auto colorReliefShaderPrelude = R"(
#define idColorReliefDrawableUBO idDrawableReservedVertexOnlyUBO
#define idColorReliefTilePropsUBO drawableReservedUBOCount
#define idColorReliefEvaluatedPropsUBO layerUBOStartId
)";
template <>
struct ShaderSource<BuiltIn::ColorReliefShader, gfx::Backend::Type::Vulkan> {
static constexpr const char* name = "ColorReliefShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 3> textures;
static constexpr auto prelude = colorReliefShaderPrelude;
static constexpr auto vertex = R"(
layout(location = 0) in ivec2 in_position;
layout(push_constant) uniform Constants {
int ubo_index;
} constant;
struct ColorReliefDrawableUBO {
mat4 matrix;
};
struct ColorReliefTilePropsUBO {
vec4 unpack;
vec2 dimension;
int color_ramp_size;
float pad_tile0;
};
layout(std140, set = LAYER_SET_INDEX, binding = idColorReliefDrawableUBO) readonly buffer ColorReliefDrawableUBOVector {
ColorReliefDrawableUBO drawable_ubo[];
} drawableVector;
layout(std140, set = LAYER_SET_INDEX, binding = idColorReliefTilePropsUBO) readonly buffer ColorReliefTilePropsUBOVector {
ColorReliefTilePropsUBO tile_props_ubo[];
} tilePropsVector;
layout(location = 0) out vec2 frag_position;
void main() {
const ColorReliefDrawableUBO drawable = drawableVector.drawable_ubo[constant.ubo_index];
const ColorReliefTilePropsUBO tileProps = tilePropsVector.tile_props_ubo[constant.ubo_index];
gl_Position = drawable.matrix * vec4(in_position, 0, 1);
applySurfaceTransform();
highp vec2 epsilon = 1.0 / tileProps.dimension;
float scale = (tileProps.dimension.x - 2.0) / tileProps.dimension.x;
frag_position = (vec2(in_position) / 8192.0) * scale + epsilon;
// Handle poles (use in_position to match GLSL a_pos)
if (float(in_position.y) < -32767.5) frag_position.y = 0.0;
if (float(in_position.y) > 32766.5) frag_position.y = 1.0;
}
)";
static constexpr auto fragment = R"(
layout(location = 0) in vec2 frag_position;
layout(location = 0) out vec4 out_color;
layout(push_constant) uniform Constants {
int ubo_index;
} constant;
struct ColorReliefTilePropsUBO {
vec4 unpack;
vec2 dimension;
int color_ramp_size;
float pad_tile0;
};
layout(std140, set = LAYER_SET_INDEX, binding = idColorReliefTilePropsUBO) readonly buffer ColorReliefTilePropsUBOVector {
ColorReliefTilePropsUBO tile_props_ubo[];
} tilePropsVector;
layout(set = LAYER_SET_INDEX, binding = idColorReliefEvaluatedPropsUBO) uniform ColorReliefEvaluatedPropsUBO {
float opacity;
float pad_eval0;
float pad_eval1;
float pad_eval2;
} props;
layout(set = DRAWABLE_IMAGE_SET_INDEX, binding = 0) uniform sampler2D image_sampler;
layout(set = DRAWABLE_IMAGE_SET_INDEX, binding = 1) uniform sampler2D elevation_stops_sampler;
layout(set = DRAWABLE_IMAGE_SET_INDEX, binding = 2) uniform sampler2D color_stops_sampler;
float getElevation(vec2 coord, vec4 unpack) {
// Convert encoded elevation value to meters
vec4 data = texture(image_sampler, coord) * 255.0;
data.a = -1.0;
return dot(data, unpack);
}
float getElevationStop(int stop, int color_ramp_size) {
// Elevation stops are plain float values, not terrain-RGB encoded
float x = (float(stop) + 0.5) / float(color_ramp_size);
return texture(elevation_stops_sampler, vec2(x, 0.5)).r;
}
vec4 getColorStop(int stop, int color_ramp_size) {
float x = (float(stop) + 0.5) / float(color_ramp_size);
return texture(color_stops_sampler, vec2(x, 0.5));
}
void main() {
#if defined(OVERDRAW_INSPECTOR)
out_color = vec4(1.0);
return;
#endif
const ColorReliefTilePropsUBO tileProps = tilePropsVector.tile_props_ubo[constant.ubo_index];
float el = getElevation(frag_position, tileProps.unpack);
// Binary search to find surrounding elevation stops (l and r indices)
int r = tileProps.color_ramp_size - 1;
int l = 0;
// Perform binary search
while (r - l > 1) {
int m = (r + l) / 2;
float el_m = getElevationStop(m, tileProps.color_ramp_size);
if (el < el_m) {
r = m;
} else {
l = m;
}
}
// Get elevation values and colors at the stops
float el_l = getElevationStop(l, tileProps.color_ramp_size);
float el_r = getElevationStop(r, tileProps.color_ramp_size);
vec4 color_l = getColorStop(l, tileProps.color_ramp_size);
vec4 color_r = getColorStop(r, tileProps.color_ramp_size);
// Interpolate color based on elevation
// Guard against division by zero when el_r == el_l
float denom = el_r - el_l;
float t = (abs(denom) < 0.0001) ? 0.0 : clamp((el - el_l) / denom, 0.0, 1.0);
out_color = props.opacity * mix(color_l, color_r, t);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -98,7 +98,7 @@ void main() {
// weight * u_intensity * GAUSS_COEF * exp(-0.5 * 3.0^2 * S^2) == ZERO
// Which solves to:
// S = sqrt(-2.0 * log(ZERO / (weight * u_intensity * GAUSS_COEF))) / 3.0
const float S = sqrt(-2.0 * log(ZERO / weight / props.intensity / GAUSS_COEF)) / 3.0;
const float S = sqrt(-2.0 * log(ZERO / (max(weight, ZERO) * max(props.intensity, ZERO) * GAUSS_COEF))) / 3.0;
// Pass the varying in units of radius
const vec2 extrude = S * unscaled_extrude;

View File

@@ -49,12 +49,19 @@ void main() {
applySurfaceTransform();
frag_position = vec2(in_texture_position) / 8192.0;
frag_position.y = 1.0 - frag_position.y; // TODO check this. prepare should ignore the flip
frag_position.y = 1.0 - frag_position.y;
}
)";
static constexpr auto fragment = R"(
#define PI 3.141592653589793
#define STANDARD 0
#define COMBINED 1
#define IGOR 2
#define MULTIDIRECTIONAL 3
#define BASIC 4
layout(location = 0) in vec2 frag_position;
layout(location = 0) out vec4 out_color;
@@ -64,7 +71,12 @@ layout(push_constant) uniform Constants {
struct HillshadeTilePropsUBO {
vec2 latrange;
vec2 light;
float exaggeration;
int method;
int num_lights;
float pad0;
float pad1;
float pad2;
};
layout(std140, set = LAYER_SET_INDEX, binding = idHillshadeTilePropsUBO) readonly buffer HillshadeTilePropsUBOVector {
@@ -72,13 +84,140 @@ layout(std140, set = LAYER_SET_INDEX, binding = idHillshadeTilePropsUBO) readonl
} tilePropsVector;
layout(set = LAYER_SET_INDEX, binding = idHillshadeEvaluatedPropsUBO) uniform HillshadeEvaluatedPropsUBO {
vec4 highlight;
vec4 shadow;
vec4 accent;
vec4 altitudes;
vec4 azimuths;
vec4 shadows[4];
vec4 highlights[4];
} props;
layout(set = DRAWABLE_IMAGE_SET_INDEX, binding = 0) uniform sampler2D image_sampler;
float get_aspect(vec2 deriv) {
return deriv.x != 0.0 ? atan(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);
}
// MapLibre's legacy hillshade algorithm (Method 0: STANDARD)
void standard_hillshade(vec2 deriv, const HillshadeTilePropsUBO tileProps) {
float azimuth = props.azimuths.x + PI;
float slope = atan(0.625 * length(deriv));
float aspect = get_aspect(deriv);
float intensity = tileProps.exaggeration;
// Scale the slope exponentially based on intensity
float base = 1.875 - intensity * 1.75;
float maxValue = 0.5 * PI;
float scaledSlope = abs(intensity - 0.5) > 1e-6 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;
float accent = cos(scaledSlope);
vec4 accent_color = (1.0 - accent) * props.accent * clamp(intensity * 2.0, 0.0, 1.0);
float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
vec4 shade_color = mix(props.shadows[0], props.highlights[0], shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
out_color = accent_color * (1.0 - shade_color.a) + shade_color;
}
// Basic directional hillshade (Method 4: BASIC)
void basic_hillshade(vec2 deriv, const HillshadeTilePropsUBO tileProps) {
deriv = deriv * tileProps.exaggeration * 2.0;
float azimuth = props.azimuths.x + PI;
float cos_az = cos(azimuth);
float sin_az = sin(azimuth);
float cos_alt = cos(props.altitudes.x);
float sin_alt = sin(props.altitudes.x);
// Calculate the cosine of the angle between the light vector and the surface normal
float cang = (sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(deriv, deriv));
float shade = clamp(cang, 0.0, 1.0);
// Blend shadow and highlight based on intensity
if (shade > 0.5) {
out_color = props.highlights[0] * (2.0 * shade - 1.0);
} else {
out_color = props.shadows[0] * (1.0 - 2.0 * shade);
}
}
// Multidirectional hillshade (Method 3: MULTIDIRECTIONAL)
void multidirectional_hillshade(vec2 deriv, const HillshadeTilePropsUBO tileProps) {
deriv = deriv * tileProps.exaggeration * 2.0;
vec4 total_color = vec4(0, 0, 0, 0);
// Access altitude and azimuth from vec4 UBOs
float altitudes[4] = float[4](props.altitudes.x, props.altitudes.y, props.altitudes.z, props.altitudes.w);
float azimuths[4] = float[4](props.azimuths.x, props.azimuths.y, props.azimuths.z, props.azimuths.w);
int num_lights = min(tileProps.num_lights, 4);
// Iterate through all light sources
for (int i = 0; i < num_lights; i++) {
float altitude = altitudes[i];
float azimuth = azimuths[i];
float cos_alt = cos(altitude);
float sin_alt = sin(altitude);
// Negate cos/sin azimuth for correct light direction
float cos_az = -cos(azimuth);
float sin_az = -sin(azimuth);
// Calculate the cosine of the angle between the light vector and the surface normal
float cang = (sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(deriv, deriv));
float shade = clamp(cang, 0.0, 1.0);
// Accumulate shadow/highlight contribution from each light
if (shade > 0.5) {
total_color += props.highlights[i] * (2.0 * shade - 1.0) / float(num_lights);
} else {
total_color += props.shadows[i] * (1.0 - 2.0 * shade) / float(num_lights);
}
}
out_color = total_color;
}
// Combined shadow and highlight method (Method 1: COMBINED)
void combined_hillshade(vec2 deriv, const HillshadeTilePropsUBO tileProps) {
// Only supports one light source (index 0)
deriv = deriv * tileProps.exaggeration * 2.0;
float azimuth = props.azimuths.x + PI;
float cos_az = cos(azimuth);
float sin_az = sin(azimuth);
float cos_alt = cos(props.altitudes.x);
float sin_alt = sin(props.altitudes.x);
// Calculate the angle between the light vector and the surface normal
float cang = acos((sin_alt - (deriv.y * cos_az * cos_alt - deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(deriv, deriv)));
cang = clamp(cang, 0.0, PI / 2.0);
// Calculate shade and highlight components from angle and slope magnitude
float shade = cang * atan(length(deriv)) * 4.0 / PI / PI;
float highlight = (PI / 2.0 - cang) * atan(length(deriv)) * 4.0 / PI / PI;
out_color = props.shadows[0] * shade + props.highlights[0] * highlight;
}
// Igor's shadow/highlight method (Method 2: IGOR)
void igor_hillshade(vec2 deriv, const HillshadeTilePropsUBO tileProps) {
// Only supports one light source (index 0)
deriv = deriv * tileProps.exaggeration * 2.0;
float aspect = get_aspect(deriv);
float azimuth = props.azimuths.x + PI;
// Slope strength is magnitude of slope vector, normalized to [0, 1]
float slope_strength = atan(length(deriv)) * 2.0 / PI;
// Aspect strength is difference between aspect and light azimuth, normalized to [0, 1]
float aspect_strength = 1.0 - abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
float shadow_strength = slope_strength * aspect_strength;
float highlight_strength = slope_strength * (1.0 - aspect_strength);
out_color = props.shadows[0] * shadow_strength + props.highlights[0] * highlight_strength;
}
void main() {
#if defined(OVERDRAW_INSPECTOR)
@@ -90,41 +229,26 @@ void main() {
vec4 pixel = texture(image_sampler, frag_position);
vec2 deriv = pixel.rg * 2.0 - 1.0;
// We divide the slope by a scale factor based on the cosin of the pixel's approximate latitude
// to account for mercator projection distortion. see #4807 for details
// Scale the derivative based on the mercator distortion at this latitude
float scaleFactor = cos(radians((tileProps.latrange[0] - tileProps.latrange[1]) * frag_position.y + tileProps.latrange[1]));
// We also multiply the slope by an arbitrary z-factor of 1.25
float slope = atan(1.25 * length(deriv) / scaleFactor);
float aspect = deriv.x != 0.0 ? atan(deriv.y, -deriv.x) : M_PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0);
float intensity = tileProps.light.x;
// We add PI to make this property match the global light object, which adds PI/2 to the light's azimuthal
// position property to account for 0deg corresponding to north/the top of the viewport in the style spec
// and the original shader was written to accept (-illuminationDirection - 90) as the azimuthal.
float azimuth = tileProps.light.y + M_PI;
// The derivative is scaled back from [0, 1] texture range to world-space slope
// Texture range [0, 1] corresponds to slope range [-4, 4]
vec2 deriv = ((pixel.rg * 8.0) - 4.0) / scaleFactor;
// We scale the slope exponentially based on intensity, using a calculation similar to
// the exponential interpolation function in the style spec:
// https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/expression/definitions/interpolate.js#L217-L228
// so that higher intensity values create more opaque hillshading.
float base = 1.875 - intensity * 1.75;
float maxValue = 0.5 * M_PI;
float scaledSlope = intensity != 0.5 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope;
// The accent color is calculated with the cosine of the slope while the shade color is calculated with the sine
// so that the accent color's rate of change eases in while the shade color's eases out.
float accent = cos(scaledSlope);
// We multiply both the accent and shade color by a clamped intensity value
// so that intensities >= 0.5 do not additionally affect the color values
// while intensity values < 0.5 make the overall color more transparent.
vec4 accent_color = (1.0 - accent) * props.accent * clamp(intensity * 2.0, 0.0, 1.0);
float shade = abs(mod((aspect + azimuth) / M_PI + 0.5, 2.0) - 1.0);
vec4 shade_color = mix(props.shadow, props.highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
vec4 color = accent_color * (1.0 - shade_color.a) + shade_color;
out_color = color;
// Dispatch to the selected hillshade method
if (tileProps.method == BASIC) {
basic_hillshade(deriv, tileProps);
} else if (tileProps.method == COMBINED) {
combined_hillshade(deriv, tileProps);
} else if (tileProps.method == IGOR) {
igor_hillshade(deriv, tileProps);
} else if (tileProps.method == MULTIDIRECTIONAL) {
multidirectional_hillshade(deriv, tileProps);
} else {
// Default to STANDARD
standard_hillshade(deriv, tileProps);
}
}
)";
};

View File

@@ -69,7 +69,7 @@ float getElevation(vec2 coord, float bias, sampler2D image_sampler, vec4 unpack)
// Convert encoded elevation value to meters
vec4 data = texture(image_sampler, coord) * 255.0;
data.a = -1.0;
return dot(data, unpack) / 4.0;
return dot(data, unpack);
}
void main() {
@@ -80,8 +80,9 @@ void main() {
#endif
const vec2 epsilon = 1.0 / tileProps.dimension;
const float tileSize = tileProps.dimension.x - 2.0;
// queried pixels:
// queried pixels (using Sobel operator kernel):
// +-----------+
// | | | |
// | a | b | c |
@@ -106,27 +107,24 @@ void main() {
float h = getElevation(frag_position + vec2(0, epsilon.y), 0.0, image_sampler, tileProps.unpack);
float i = getElevation(frag_position + vec2(epsilon.x, epsilon.y), 0.0, image_sampler, tileProps.unpack);
// here we divide the x and y slopes by 8 * pixel size
// where pixel size (aka meters/pixel) is:
// circumference of the world / (pixels per tile * number of tiles)
// which is equivalent to: 8 * 40075016.6855785 / (512 * pow(2, u_zoom))
// which can be reduced to: pow(2, 19.25619978527 - u_zoom)
// we want to vertically exaggerate the hillshading though, because otherwise
// it is barely noticeable at low zooms. to do this, we multiply this by some
// scale factor pow(2, (u_zoom - u_maxzoom) * a) where a is an arbitrary value
// Here we use a=0.3 which works out to the expression below. see
// nickidlugash's awesome breakdown for more info
// https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556
float exaggeration = tileProps.zoom < 2.0 ? 0.4 : tileProps.zoom < 4.5 ? 0.35 : 0.3;
// Convert the raw pixel-space derivative (slope) into world-space slope.
// The conversion factor is: tileSize / (8 * meters_per_pixel).
// meters_per_pixel is calculated as pow(2.0, 28.2562 - u_zoom).
// The exaggeration factor is applied to scale the effect at lower zooms.
float exaggerationFactor = tileProps.zoom < 2.0 ? 0.4 : tileProps.zoom < 4.5 ? 0.35 : 0.3;
float exaggeration = tileProps.zoom < 15.0 ? (tileProps.zoom - 15.0) * exaggerationFactor : 0.0;
vec2 deriv = vec2(
(c + f + f + i) - (a + d + d + g),
(g + h + h + i) - (a + b + b + c)
) / pow(2.0, (tileProps.zoom - tileProps.maxzoom) * exaggeration + 19.2562 - tileProps.zoom);
) * tileSize / pow(2.0, exaggeration + (28.2562 - tileProps.zoom));
// Encode the derivative into the color channels (r and g)
// The derivative is scaled from world-space slope to the range [0, 1] for texture storage.
// The maximum possible world-space derivative is assumed to be 4 (hence division by 8.0).
out_color = clamp(vec4(
deriv.x / 2.0 + 0.5,
deriv.y / 2.0 + 0.5,
deriv.x / 8.0 + 0.5,
deriv.y / 8.0 + 0.5,
1.0,
1.0), 0.0, 1.0);
}

View File

@@ -0,0 +1,163 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/color_relief_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::ColorReliefShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "ColorReliefShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 3> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec2<i32>,
@location(6) texture_pos: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) frag_position: vec2<f32>,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
struct ColorReliefDrawableUBO {
matrix: mat4x4<f32>,
};
struct ColorReliefTilePropsUBO {
unpack: vec4<f32>,
dimension: vec2<f32>,
color_ramp_size: i32,
pad_tile0: f32,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<ColorReliefDrawableUBO>;
@group(0) @binding(4) var<storage, read> tilePropsVector: array<ColorReliefTilePropsUBO>;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let tileProps = tilePropsVector[globalIndex.value];
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
let a_pos = vec2<f32>(f32(in.position.x), f32(in.position.y));
let epsilon = vec2<f32>(1.0, 1.0) / tileProps.dimension;
let scale = (tileProps.dimension.x - 2.0) / tileProps.dimension.x;
out.frag_position = (a_pos / 8192.0) * scale + epsilon;
// Handle poles
out.frag_position.y = select(out.frag_position.y, 0.0, a_pos.y < -32767.5);
out.frag_position.y = select(out.frag_position.y, 1.0, a_pos.y > 32766.5);
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) frag_position: vec2<f32>,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
struct ColorReliefTilePropsUBO {
unpack: vec4<f32>,
dimension: vec2<f32>,
color_ramp_size: i32,
pad_tile0: f32,
};
struct ColorReliefEvaluatedPropsUBO {
opacity: f32,
pad_eval0: f32,
pad_eval1: f32,
pad_eval2: f32,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(4) var<storage, read> tilePropsVector: array<ColorReliefTilePropsUBO>;
@group(0) @binding(5) var<uniform> props: ColorReliefEvaluatedPropsUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var dem_texture: texture_2d<f32>;
@group(1) @binding(2) var elevation_stops_texture: texture_2d<f32>;
@group(1) @binding(3) var color_stops_texture: texture_2d<f32>;
fn getElevation(coord: vec2<f32>, unpack: vec4<f32>) -> f32 {
var data = textureSample(dem_texture, texture_sampler, coord) * 255.0;
data.a = -1.0;
return dot(data, unpack);
}
fn getElevationStop(stop: i32, color_ramp_size: i32) -> f32 {
// Use textureLoad for RGBA32Float texture (not filterable in WebGPU)
return textureLoad(elevation_stops_texture, vec2<i32>(stop, 0), 0).r;
}
fn getColorStop(stop: i32, color_ramp_size: i32) -> vec4<f32> {
let x = (f32(stop) + 0.5) / f32(color_ramp_size);
return textureSample(color_stops_texture, texture_sampler, vec2<f32>(x, 0.5));
}
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef OVERDRAW_INSPECTOR
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
#endif
let tileProps = tilePropsVector[globalIndex.value];
let el = getElevation(in.frag_position, tileProps.unpack);
// Binary search to find surrounding elevation stops
var r: i32 = tileProps.color_ramp_size - 1;
var l: i32 = 0;
loop {
if (r - l <= 1) { break; }
let m = (r + l) / 2;
let el_m = getElevationStop(m, tileProps.color_ramp_size);
if (el < el_m) {
r = m;
} else {
l = m;
}
}
// Get elevation values and colors at the stops
let el_l = getElevationStop(l, tileProps.color_ramp_size);
let el_r = getElevationStop(r, tileProps.color_ramp_size);
let color_l = getColorStop(l, tileProps.color_ramp_size);
let color_r = getColorStop(r, tileProps.color_ramp_size);
// Interpolate color based on elevation
// Guard against division by zero when el_r == el_l
let denom = el_r - el_l;
var t: f32 = 0.0;
if (abs(denom) >= 0.0001) {
t = clamp((el - el_l) / denom, 0.0, 1.0);
}
let final_color = mix(color_l, color_r, t) * props.opacity;
return vec4<f32>(final_color);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -51,6 +51,12 @@ fn main(in: VertexInput) -> VertexOutput {
)";
static constexpr auto fragment = R"(
const STANDARD: i32 = 0;
const COMBINED: i32 = 1;
const IGOR: i32 = 2;
const MULTIDIRECTIONAL: i32 = 3;
const BASIC: i32 = 4;
struct FragmentInput {
@location(0) tex_coord: vec2<f32>,
};
@@ -62,13 +68,20 @@ struct GlobalIndexUBO {
struct HillshadeTilePropsUBO {
latrange: vec2<f32>,
light: vec2<f32>,
exaggeration: f32,
method: i32,
num_lights: i32,
pad0: f32,
pad1: f32,
pad2: f32,
};
struct HillshadeEvaluatedPropsUBO {
highlight: vec4<f32>,
shadow: vec4<f32>,
accent: vec4<f32>,
altitudes: vec4<f32>,
azimuths: vec4<f32>,
shadows: array<vec4<f32>, 4>,
highlights: array<vec4<f32>, 4>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@@ -77,6 +90,108 @@ struct HillshadeEvaluatedPropsUBO {
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var hillshade_texture: texture_2d<f32>;
fn get_aspect(deriv: vec2<f32>) -> f32 {
let aspectDefault = 0.5 * PI * select(-1.0, 1.0, deriv.y > 0.0);
return select(aspectDefault, atan2(deriv.y, -deriv.x), deriv.x != 0.0);
}
fn standard_hillshade(deriv: vec2<f32>, tileProps: HillshadeTilePropsUBO) -> vec4<f32> {
let azimuth = props.azimuths.x + PI;
let slope = atan(0.625 * length(deriv));
let aspect = get_aspect(deriv);
let intensity = tileProps.exaggeration;
let base = 1.875 - intensity * 1.75;
let maxValue = 0.5 * PI;
let denom = pow(base, maxValue) - 1.0;
let useNonLinear = abs(intensity - 0.5) > 1e-6;
let scaledSlope = select(slope, ((pow(base, slope) - 1.0) / denom) * maxValue, useNonLinear);
let accent = cos(scaledSlope);
let accentColor = (1.0 - accent) * props.accent * clamp(intensity * 2.0, 0.0, 1.0);
let shade = abs(glMod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
let shadeColor = mix(props.shadows[0], props.highlights[0], shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
return accentColor * (1.0 - shadeColor.a) + shadeColor;
}
fn basic_hillshade(deriv: vec2<f32>, tileProps: HillshadeTilePropsUBO) -> vec4<f32> {
let scaled_deriv = deriv * tileProps.exaggeration * 2.0;
let azimuth = props.azimuths.x + PI;
let cos_az = cos(azimuth);
let sin_az = sin(azimuth);
let cos_alt = cos(props.altitudes.x);
let sin_alt = sin(props.altitudes.x);
let cang = (sin_alt - (scaled_deriv.y * cos_az * cos_alt - scaled_deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(scaled_deriv, scaled_deriv));
let shade = clamp(cang, 0.0, 1.0);
return select(props.shadows[0] * (1.0 - 2.0 * shade),
props.highlights[0] * (2.0 * shade - 1.0),
shade > 0.5);
}
fn multidirectional_hillshade(deriv: vec2<f32>, tileProps: HillshadeTilePropsUBO) -> vec4<f32> {
let scaled_deriv = deriv * tileProps.exaggeration * 2.0;
var total_color = vec4<f32>(0.0, 0.0, 0.0, 0.0);
let num_lights = min(tileProps.num_lights, 4);
for (var i: i32 = 0; i < num_lights; i = i + 1) {
let altitude = select(select(select(props.altitudes.x, props.altitudes.y, i == 1), props.altitudes.z, i == 2), props.altitudes.w, i == 3);
let azimuth = select(select(select(props.azimuths.x, props.azimuths.y, i == 1), props.azimuths.z, i == 2), props.azimuths.w, i == 3);
let cos_alt = cos(altitude);
let sin_alt = sin(altitude);
let cos_az = -cos(azimuth);
let sin_az = -sin(azimuth);
let cang = (sin_alt - (scaled_deriv.y * cos_az * cos_alt - scaled_deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(scaled_deriv, scaled_deriv));
let shade = clamp(cang, 0.0, 1.0);
if (shade > 0.5) {
total_color = total_color + props.highlights[i] * (2.0 * shade - 1.0) / f32(num_lights);
} else {
total_color = total_color + props.shadows[i] * (1.0 - 2.0 * shade) / f32(num_lights);
}
}
return total_color;
}
fn combined_hillshade(deriv: vec2<f32>, tileProps: HillshadeTilePropsUBO) -> vec4<f32> {
let scaled_deriv = deriv * tileProps.exaggeration * 2.0;
let azimuth = props.azimuths.x + PI;
let cos_az = cos(azimuth);
let sin_az = sin(azimuth);
let cos_alt = cos(props.altitudes.x);
let sin_alt = sin(props.altitudes.x);
let cang = acos((sin_alt - (scaled_deriv.y * cos_az * cos_alt - scaled_deriv.x * sin_az * cos_alt)) / sqrt(1.0 + dot(scaled_deriv, scaled_deriv)));
let clamped_cang = clamp(cang, 0.0, PI / 2.0);
let shade = clamped_cang * atan(length(scaled_deriv)) * 4.0 / PI / PI;
let highlight = (PI / 2.0 - clamped_cang) * atan(length(scaled_deriv)) * 4.0 / PI / PI;
return props.shadows[0] * shade + props.highlights[0] * highlight;
}
fn igor_hillshade(deriv: vec2<f32>, tileProps: HillshadeTilePropsUBO) -> vec4<f32> {
let scaled_deriv = deriv * tileProps.exaggeration * 2.0;
let aspect = get_aspect(scaled_deriv);
let azimuth = props.azimuths.x + PI;
let slope_strength = atan(length(scaled_deriv)) * 2.0 / PI;
let aspect_strength = 1.0 - abs(glMod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
let shadow_strength = slope_strength * aspect_strength;
let highlight_strength = slope_strength * (1.0 - aspect_strength);
return props.shadows[0] * shadow_strength + props.highlights[0] * highlight_strength;
}
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef OVERDRAW_INSPECTOR
@@ -86,34 +201,23 @@ fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let tileProps = tilePropsVector[globalIndex.value];
let pixel = textureSample(hillshade_texture, texture_sampler, in.tex_coord);
let deriv = pixel.rg * 2.0 - vec2<f32>(1.0, 1.0);
let latRange = tileProps.latrange;
let latitude = (latRange.x - latRange.y) * in.tex_coord.y + latRange.y;
let scaleFactor = cos(radians(latitude));
let slope = atan(1.25 * length(deriv) / scaleFactor);
let aspectDefault = 0.5 * PI * select(-1.0, 1.0, deriv.y > 0.0);
let aspect = select(aspectDefault, atan2(deriv.y, -deriv.x), deriv.x != 0.0);
let intensity = tileProps.light.x;
let azimuth = tileProps.light.y + PI;
let deriv = ((pixel.rg * 8.0) - vec2<f32>(4.0, 4.0)) / scaleFactor;
let base = 1.875 - intensity * 1.75;
let maxValue = 0.5 * PI;
let denom = pow(base, maxValue) - 1.0;
let useLinear = abs(intensity - 0.5) < 1e-6;
let scaledSlope = select(((pow(base, slope) - 1.0) / denom) * maxValue,
slope,
useLinear);
let accent = cos(scaledSlope);
let accentColor = (1.0 - accent) * props.accent * clamp(intensity * 2.0, 0.0, 1.0);
let shade = abs(glMod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
let shadeColor = mix(props.shadow, props.highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
let color = accentColor * (1.0 - shadeColor.a) + shadeColor;
return color;
if (tileProps.method == BASIC) {
return basic_hillshade(deriv, tileProps);
} else if (tileProps.method == COMBINED) {
return combined_hillshade(deriv, tileProps);
} else if (tileProps.method == IGOR) {
return igor_hillshade(deriv, tileProps);
} else if (tileProps.method == MULTIDIRECTIONAL) {
return multidirectional_hillshade(deriv, tileProps);
} else {
return standard_hillshade(deriv, tileProps);
}
}
)";
};

View File

@@ -70,7 +70,7 @@ struct HillshadePrepareTilePropsUBO {
fn getElevation(coord: vec2<f32>) -> f32 {
var data = textureSample(dem_texture, texture_sampler, coord) * 255.0;
data.a = -1.0;
return dot(data, tileProps.unpack) / 4.0;
return dot(data, tileProps.unpack);
}
@fragment
@@ -80,6 +80,7 @@ fn main(in: FragmentInput) -> @location(0) vec4<f32> {
#endif
let epsilon = vec2<f32>(1.0, 1.0) / tileProps.dimension;
let tileSize = tileProps.dimension.x - 2.0;
let a = getElevation(in.tex_coord + vec2<f32>(-epsilon.x, -epsilon.y));
let b = getElevation(in.tex_coord + vec2<f32>(0.0, -epsilon.y));
@@ -90,20 +91,24 @@ fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let h = getElevation(in.tex_coord + vec2<f32>(0.0, epsilon.y));
let i = getElevation(in.tex_coord + vec2<f32>(epsilon.x, epsilon.y));
var exaggeration = 0.3;
var exaggerationFactor = 0.3;
if (tileProps.zoom < 4.5) {
exaggeration = 0.35;
exaggerationFactor = 0.35;
}
if (tileProps.zoom < 2.0) {
exaggeration = 0.4;
exaggerationFactor = 0.4;
}
var exaggeration = 0.0;
if (tileProps.zoom < 15.0) {
exaggeration = (tileProps.zoom - 15.0) * exaggerationFactor;
}
let denom = pow(2.0, (tileProps.zoom - tileProps.maxzoom) * exaggeration + 19.2562 - tileProps.zoom);
let denom = pow(2.0, exaggeration + (28.2562 - tileProps.zoom));
let deriv = vec2<f32>((c + f + f + i) - (a + d + d + g),
(g + h + h + i) - (a + b + b + c)) / denom;
(g + h + h + i) - (a + b + b + c)) * tileSize / denom;
let color = clamp(vec4<f32>(deriv.x / 2.0 + 0.5,
deriv.y / 2.0 + 0.5,
let color = clamp(vec4<f32>(deriv.x / 8.0 + 0.5,
deriv.y / 8.0 + 0.5,
1.0,
1.0),
vec4<f32>(0.0, 0.0, 0.0, 0.0),

View File

@@ -66,7 +66,8 @@ public:
ReadOnlyStorageBuffer,
StorageBuffer,
Sampler,
Texture
Texture,
UnfilterableTexture // For RGBA32Float textures that require textureLoad instead of textureSample
};
struct BindingInfo {

View File

@@ -46,6 +46,11 @@ struct Converter<Color> {
std::optional<Color> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<std::vector<Color>> {
std::optional<std::vector<Color>> operator()(const Convertible& value, Error& error) const;
};
template <>
struct Converter<Padding> {
std::optional<Padding> operator()(const Convertible& value, Error& error) const;

View File

@@ -51,6 +51,15 @@ public:
feature(feature_),
colorRampParameter(std::move(colorRampParameter_)) {}
EvaluationContext(std::optional<float> zoom_,
GeometryTileFeature const* feature_,
std::optional<double> colorRampParameter_,
std::optional<float> elevation_) noexcept
: zoom(std::move(zoom_)),
feature(feature_),
colorRampParameter(std::move(colorRampParameter_)),
elevation(std::move(elevation_)) {}
EvaluationContext& withFormattedSection(const Value* formattedSection_) noexcept {
formattedSection = formattedSection_;
return *this;
@@ -71,10 +80,16 @@ public:
return *this;
};
EvaluationContext& withElevation(float elevation_) noexcept {
elevation = elevation_;
return *this;
};
std::optional<float> zoom;
std::optional<mbgl::Value> accumulated;
GeometryTileFeature const* feature = nullptr;
std::optional<double> colorRampParameter;
std::optional<float> elevation;
// Contains formatted section object, std::unordered_map<std::string, Value>.
const Value* formattedSection = nullptr;
const FeatureState* featureState = nullptr;
@@ -202,7 +217,8 @@ enum class Dependency : uint32_t {
Bind = 1 << 4, // Create variable binding ("let")
Var = 1 << 5, // Use variable binding
Override = 1 << 6, // Property override
MaskCount = 7,
Elevation = 1 << 7, // Elevation from DEM
MaskCount = 8,
All = (1 << MaskCount) - 1,
};

View File

@@ -0,0 +1,59 @@
// clang-format off
// This file is generated. Do not edit.
#pragma once
#include <mbgl/style/color_ramp_property_value.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/style/filter.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/util/color.hpp>
namespace mbgl {
namespace style {
class TransitionOptions;
class ColorReliefLayer final : public Layer {
public:
ColorReliefLayer(const std::string& layerID, const std::string& sourceID);
~ColorReliefLayer() override;
// Paint properties
static ColorRampPropertyValue getDefaultColorReliefColor();
const ColorRampPropertyValue& getColorReliefColor() const;
void setColorReliefColor(const ColorRampPropertyValue&);
void setColorReliefColorTransition(const TransitionOptions&);
TransitionOptions getColorReliefColorTransition() const;
static PropertyValue<float> getDefaultColorReliefOpacity();
const PropertyValue<float>& getColorReliefOpacity() const;
void setColorReliefOpacity(const PropertyValue<float>&);
void setColorReliefOpacityTransition(const TransitionOptions&);
TransitionOptions getColorReliefOpacityTransition() const;
// Private implementation
class Impl;
const Impl& impl() const;
Mutable<Impl> mutableImpl() const;
ColorReliefLayer(Immutable<Impl>);
std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
protected:
// Dynamic properties
std::optional<conversion::Error> setPropertyInternal(const std::string& name, const conversion::Convertible& value) final;
StyleProperty getProperty(const std::string& name) const final;
Value serialize() const final;
Mutable<Layer::Impl> mutableBaseImpl() const final;
};
} // namespace style
} // namespace mbgl
// clang-format on

View File

@@ -33,27 +33,39 @@ public:
void setHillshadeExaggerationTransition(const TransitionOptions&);
TransitionOptions getHillshadeExaggerationTransition() const;
static PropertyValue<Color> getDefaultHillshadeHighlightColor();
const PropertyValue<Color>& getHillshadeHighlightColor() const;
void setHillshadeHighlightColor(const PropertyValue<Color>&);
static PropertyValue<std::vector<Color>> getDefaultHillshadeHighlightColor();
const PropertyValue<std::vector<Color>>& getHillshadeHighlightColor() const;
void setHillshadeHighlightColor(const PropertyValue<std::vector<Color>>&);
void setHillshadeHighlightColorTransition(const TransitionOptions&);
TransitionOptions getHillshadeHighlightColorTransition() const;
static PropertyValue<std::vector<float>> getDefaultHillshadeIlluminationAltitude();
const PropertyValue<std::vector<float>>& getHillshadeIlluminationAltitude() const;
void setHillshadeIlluminationAltitude(const PropertyValue<std::vector<float>>&);
void setHillshadeIlluminationAltitudeTransition(const TransitionOptions&);
TransitionOptions getHillshadeIlluminationAltitudeTransition() const;
static PropertyValue<HillshadeIlluminationAnchorType> getDefaultHillshadeIlluminationAnchor();
const PropertyValue<HillshadeIlluminationAnchorType>& getHillshadeIlluminationAnchor() const;
void setHillshadeIlluminationAnchor(const PropertyValue<HillshadeIlluminationAnchorType>&);
void setHillshadeIlluminationAnchorTransition(const TransitionOptions&);
TransitionOptions getHillshadeIlluminationAnchorTransition() const;
static PropertyValue<float> getDefaultHillshadeIlluminationDirection();
const PropertyValue<float>& getHillshadeIlluminationDirection() const;
void setHillshadeIlluminationDirection(const PropertyValue<float>&);
static PropertyValue<std::vector<float>> getDefaultHillshadeIlluminationDirection();
const PropertyValue<std::vector<float>>& getHillshadeIlluminationDirection() const;
void setHillshadeIlluminationDirection(const PropertyValue<std::vector<float>>&);
void setHillshadeIlluminationDirectionTransition(const TransitionOptions&);
TransitionOptions getHillshadeIlluminationDirectionTransition() const;
static PropertyValue<Color> getDefaultHillshadeShadowColor();
const PropertyValue<Color>& getHillshadeShadowColor() const;
void setHillshadeShadowColor(const PropertyValue<Color>&);
static PropertyValue<HillshadeMethodType> getDefaultHillshadeMethod();
const PropertyValue<HillshadeMethodType>& getHillshadeMethod() const;
void setHillshadeMethod(const PropertyValue<HillshadeMethodType>&);
void setHillshadeMethodTransition(const TransitionOptions&);
TransitionOptions getHillshadeMethodTransition() const;
static PropertyValue<std::vector<Color>> getDefaultHillshadeShadowColor();
const PropertyValue<std::vector<Color>>& getHillshadeShadowColor() const;
void setHillshadeShadowColor(const PropertyValue<std::vector<Color>>&);
void setHillshadeShadowColorTransition(const TransitionOptions&);
TransitionOptions getHillshadeShadowColorTransition() const;

View File

@@ -9,7 +9,7 @@
#pragma once
<% if (type === 'heatmap' || type === 'line') { -%>
<% if (type === 'heatmap' || type === 'line' || type === 'color-relief') { -%>
#include <mbgl/style/color_ramp_property_value.hpp>
<% } -%>
#include <mbgl/style/layer.hpp>

View File

@@ -48,6 +48,14 @@ enum class HillshadeIlluminationAnchorType : bool {
Viewport
};
enum class HillshadeMethodType : uint8_t {
Standard,
Combined,
Igor,
Multidirectional,
Basic
};
enum class TranslateAnchorType : bool {
Map,
Viewport

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

View File

@@ -0,0 +1,59 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "color-relief",
"type": "color-relief",
"source": "source",
"paint": {
"color-relief-opacity": 1,
"color-relief-color": [
"interpolate",
["linear"],
["elevation"],
400, "rgb(112, 209, 255)",
494.1176471, "rgb(113, 211, 247)",
588.2352941, "rgb(114, 212, 234)",
682.3529412, "rgb(117, 213, 222)",
776.4705882, "rgb(120, 214, 209)",
870.5882353, "rgb(124, 215, 196)",
964.7058824, "rgb(130, 215, 183)",
1058.823529, "rgb(138, 215, 169)",
1152.941176, "rgb(149, 214, 155)",
1247.058824, "rgb(163, 212, 143)",
1341.176471, "rgb(178, 209, 134)",
1435.294118, "rgb(193, 205, 127)",
1529.411765, "rgb(207, 202, 121)",
1623.529412, "rgb(220, 197, 118)",
1717.647059, "rgb(233, 193, 118)",
1811.764706, "rgb(244, 188, 120)",
1905.882353, "rgb(255, 183, 124)",
2000, "rgb(255, 178, 129)"
]
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -0,0 +1,50 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies color-relief rendering at low zoom levels (zoom 1) where exaggeration scaling is applied"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "color-relief",
"type": "color-relief",
"source": "source",
"paint": {
"color-relief-opacity": 1,
"color-relief-color": [
"interpolate",
["linear"],
["elevation"],
0, "#006600",
500, "#00FF00",
1000, "#FFFF00",
2000, "#FF8800",
3000, "#FF0000",
5000, "#FFFFFF"
]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -0,0 +1,43 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "color-relief",
"type": "color-relief",
"source": "source",
"paint": {
"color-relief-opacity": 0.5,
"color-relief-color": [
"interpolate",
["linear"],
["elevation"],
400, "#F00",
800, "#AA0",
1000, "#AF0",
1200, "#0F0",
1400, "#0AA",
1600, "#00F",
2000, "#C0C"
]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -0,0 +1,43 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "color-relief",
"type": "color-relief",
"source": "source",
"paint": {
"color-relief-opacity": 1,
"color-relief-color": [
"interpolate",
["linear"],
["elevation"],
400, "#F00",
800, "#AA0",
1000, "#AF0",
1200, "#0F0",
1400, "#0AA",
1600, "#00F",
2000, "#C0C"
]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -0,0 +1,43 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "color-relief",
"type": "color-relief",
"source": "source",
"paint": {
"color-relief-opacity": 1,
"color-relief-color": [
"interpolate",
["linear"],
["elevation"],
400, "#F00C",
800, "#AA0A",
1000, "#AF09",
1200, "#0F08",
1400, "#0AA7",
1600, "#00F6",
2000, "#C0C4"
]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,66 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies combined hillshade and color-relief rendering at low zoom levels (zoom 1) where exaggeration scaling is applied"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"dem-hillshade": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
},
"dem-color-relief": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "color-relief",
"type": "color-relief",
"source": "dem-color-relief",
"paint": {
"color-relief-opacity": 1,
"color-relief-color": [
"interpolate",
["linear"],
["elevation"],
0, "#006600",
500, "#00FF00",
1000, "#FFFF00",
2000, "#FF8800",
3000, "#FF0000",
5000, "#FFFFFF"
]
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "dem-hillshade",
"paint": {
"hillshade-method": "standard"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -0,0 +1,39 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "basic",
"hillshade-illumination-altitude": 0
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@@ -0,0 +1,39 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "basic",
"hillshade-illumination-altitude": 90
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@@ -0,0 +1,39 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "combined",
"hillshade-illumination-altitude": 60
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,39 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies hillshade rendering with basic method at low zoom levels (zoom 1) where exaggeration scaling is applied"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "basic"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,39 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies hillshade rendering with combined method at low zoom levels (zoom 1) where exaggeration scaling is applied"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "combined"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,36 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies hillshade rendering at low zoom levels (zoom 1) where exaggeration scaling is applied to make hillshading more visible"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,39 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies hillshade rendering with igor method at low zoom levels (zoom 1) where exaggeration scaling is applied"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "igor"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -0,0 +1,42 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies hillshade rendering with multidirectional method at low zoom levels (zoom 1) where exaggeration scaling is applied"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "multidirectional",
"hillshade-highlight-color": ["#FF4000", "#FFFF00", "#40ff00", "#00FF80"],
"hillshade-shadow-color": ["#00bfff", "#0000ff", "#bf00ff", "#FF0080"],
"hillshade-illumination-direction": [270, 315, 0, 45]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,39 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies hillshade rendering with standard method at low zoom levels (zoom 1) where exaggeration scaling is applied"
}
},
"center": [0, 45],
"zoom": 1,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 512
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "standard"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -0,0 +1,36 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies that setting a lower maxzoom on the raster-dem source produces the same output as a higher maxzoom (hillshade-accent-color/default) when the map zoom level is lower than the maxzoom (i.e., maxzoom does not alter rendering at lower zooms)"
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 12,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1,36 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256,
"description": "This test verifies that setting a maximum zoom on the raster-dem source lower than the layer results in overzooming the source."
}
},
"center": [-113.26903, 35.9654],
"zoom": 13,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 12,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

View File

@@ -0,0 +1,38 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "basic"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View File

@@ -0,0 +1,38 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "combined"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -0,0 +1,38 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "igor"
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View File

@@ -0,0 +1,41 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"width": 256
}
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "multidirectional",
"hillshade-highlight-color": ["#FF4000", "#FFFF00", "#40ff00", "#00FF80"],
"hillshade-shadow-color": ["#00bfff", "#0000ff", "#bf00ff", "#FF0080"],
"hillshade-illumination-direction": [270,315,0,45]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -0,0 +1,74 @@
{
"version": 8,
"metadata": {
"test": {
"width": 256,
"height": 256,
"description": "This test verifies that changing hillshade array properties one at a time (causing temporary length mismatch) does not cause crashes or rendering artifacts. Starts with 4 lights, changes direction to 1, then updates colors to 1. Using a dramatic 4->1 change makes any issues more visible (per HarelM's suggestion).",
"operations": [
[
"setPaintProperty",
"hillshade",
"hillshade-illumination-direction",
315
],
[
"wait"
],
[
"setPaintProperty",
"hillshade",
"hillshade-highlight-color",
"#FFFFFF"
],
[
"wait"
],
[
"setPaintProperty",
"hillshade",
"hillshade-shadow-color",
"#000000"
],
[
"wait"
]
]
}
},
"transition": {
"duration": 0
},
"center": [-113.26903, 35.9654],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrain.png"
],
"maxzoom": 15,
"tileSize": 256
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "gray"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source",
"paint": {
"hillshade-method": "multidirectional",
"hillshade-highlight-color": ["#FF4000", "#FFFF00", "#40ff00", "#00FF80"],
"hillshade-shadow-color": ["#00bfff", "#0000ff", "#bf00ff", "#FF0080"],
"hillshade-illumination-direction": [270, 315, 0, 45]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

2500
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
"license": "BSD-2-Clause",
"dependencies": {
"@actions/core": "^1.11.1",
"@maplibre/maplibre-gl-style-spec": "^20.3.1",
"@maplibre/maplibre-gl-style-spec": "^24.3.1",
"minimatch": "^10.2.2",
"npm-run-all": "^4.1.5",
"simple-git": "^3.25.0"

View File

@@ -147,6 +147,8 @@ add_library(maplibre SHARED
${PROJECT_SOURCE_DIR}/style/layers/background_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/circle_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/circle_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/color_relief_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/color_relief_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/custom_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/custom_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/fill_extrusion_layer.cpp

View File

@@ -101,6 +101,17 @@ Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, std::vect
return result;
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, std::vector<Color>>::operator()(
jni::JNIEnv& env, const std::vector<Color>& value) const {
auto result = jni::Array<jni::String>::New(env, value.size());
for (std::size_t i = 0; i < value.size(); i++) {
result.Set(env, i, jni::Make<jni::String>(env, value.at(i).stringify()));
}
return result;
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, style::expression::Image>::operator()(
jni::JNIEnv& env, const style::expression::Image& value) const {
return jni::Make<jni::String>(env, value.id());

View File

@@ -111,6 +111,11 @@ struct Converter<jni::Local<jni::Object<>>, std::vector<double>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::vector<double>& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, std::vector<Color>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::vector<Color>& value) const;
};
template <class T>
struct Converter<jni::Local<jni::Object<>>, T, typename std::enable_if_t<std::is_enum<T>::value>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const T& value) const {

View File

@@ -0,0 +1,130 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
#include "color_relief_layer.hpp"
#include <string>
#include "../conversion/property_value.hpp"
#include "../conversion/transition_options.hpp"
#include <mbgl/style/layer_impl.hpp>
namespace mbgl {
namespace android {
inline mbgl::style::ColorReliefLayer& toColorReliefLayer(mbgl::style::Layer& layer) {
return static_cast<mbgl::style::ColorReliefLayer&>(layer);
}
/**
* Creates an owning peer object (for layers not attached to the map) from the JVM side
*/
ColorReliefLayer::ColorReliefLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId)
: Layer(std::make_unique<mbgl::style::ColorReliefLayer>(jni::Make<std::string>(env, layerId),
jni::Make<std::string>(env, sourceId))) {}
/**
* Creates a non-owning peer object (for layers currently attached to the map)
*/
ColorReliefLayer::ColorReliefLayer(mbgl::style::ColorReliefLayer& coreLayer)
: Layer(coreLayer) {}
/**
* Creates an owning peer object (for layers not attached to the map)
*/
ColorReliefLayer::ColorReliefLayer(std::unique_ptr<mbgl::style::ColorReliefLayer> coreLayer)
: Layer(std::move(coreLayer)) {}
ColorReliefLayer::~ColorReliefLayer() = default;
// Property getters
jni::Local<jni::Object<>> ColorReliefLayer::getColorReliefOpacity(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
auto layer = layerPtr.get();
if (!layer) {
return std::move(
*convert<jni::Local<jni::Object<>>>(env, style::ColorReliefLayer::getDefaultColorReliefOpacity()));
}
return std::move(*convert<jni::Local<jni::Object<>>>(env, toColorReliefLayer(*layer).getColorReliefOpacity()));
}
jni::Local<jni::Object<TransitionOptions>> ColorReliefLayer::getColorReliefOpacityTransition(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
auto layer = layerPtr.get();
if (!layer) {
return std::move(*convert<jni::Local<jni::Object<TransitionOptions>>>(env, mbgl::style::TransitionOptions()));
}
mbgl::style::TransitionOptions options = toColorReliefLayer(*layer).getColorReliefOpacityTransition();
return std::move(*convert<jni::Local<jni::Object<TransitionOptions>>>(env, options));
}
void ColorReliefLayer::setColorReliefOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) {
auto layer = layerPtr.get();
if (!layer) {
return;
}
mbgl::style::TransitionOptions options;
options.duration.emplace(mbgl::Milliseconds(duration));
options.delay.emplace(mbgl::Milliseconds(delay));
toColorReliefLayer(*layer).setColorReliefOpacityTransition(options);
}
jni::Local<jni::Object<>> ColorReliefLayer::getColorReliefColor(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
auto layer = layerPtr.get();
if (!layer) {
return std::move(
*convert<jni::Local<jni::Object<>>>(env, style::ColorReliefLayer::getDefaultColorReliefColor()));
}
return std::move(*convert<jni::Local<jni::Object<>>>(env, toColorReliefLayer(*layer).getColorReliefColor()));
}
// ColorReliefJavaLayerPeerFactory
ColorReliefJavaLayerPeerFactory::~ColorReliefJavaLayerPeerFactory() = default;
namespace {
jni::Local<jni::Object<Layer>> createJavaPeer(jni::JNIEnv& env, Layer* layer) {
static auto& javaClass = jni::Class<ColorReliefLayer>::Singleton(env);
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(layer));
}
} // namespace
jni::Local<jni::Object<Layer>> ColorReliefJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env,
mbgl::style::Layer& layer) {
assert(layer.baseImpl->getTypeInfo() == getTypeInfo());
return createJavaPeer(env, new ColorReliefLayer(toColorReliefLayer(layer)));
}
jni::Local<jni::Object<Layer>> ColorReliefJavaLayerPeerFactory::createJavaLayerPeer(
jni::JNIEnv& env, std::unique_ptr<mbgl::style::Layer> layer) {
assert(layer->baseImpl->getTypeInfo() == getTypeInfo());
return createJavaPeer(env,
new ColorReliefLayer(std::unique_ptr<mbgl::style::ColorReliefLayer>(
static_cast<mbgl::style::ColorReliefLayer*>(layer.release()))));
}
void ColorReliefJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) {
// Lookup the class
static auto& javaClass = jni::Class<ColorReliefLayer>::Singleton(env);
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
// Register the peer
jni::RegisterNativePeer<ColorReliefLayer>(
env,
javaClass,
"nativePtr",
jni::MakePeer<ColorReliefLayer, jni::String&, jni::String&>,
"initialize",
"finalize",
METHOD(&ColorReliefLayer::getColorReliefOpacityTransition, "nativeGetColorReliefOpacityTransition"),
METHOD(&ColorReliefLayer::setColorReliefOpacityTransition, "nativeSetColorReliefOpacityTransition"),
METHOD(&ColorReliefLayer::getColorReliefOpacity, "nativeGetColorReliefOpacity"),
METHOD(&ColorReliefLayer::getColorReliefColor, "nativeGetColorReliefColor"));
}
} // namespace android
} // namespace mbgl

Some files were not shown because too many files have changed in this diff Show More