core: Update Hillshade Algorithms and add Color-Relief Layer support (#3965)
17
.github/workflows/node-ci.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
15
include/mbgl/layermanager/color_relief_layer_factory.hpp
Normal 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
|
||||
32
include/mbgl/shaders/color_relief_layer_ubo.hpp
Normal 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
|
||||
119
include/mbgl/shaders/gl/color_relief.hpp
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
170
include/mbgl/shaders/mtl/color_relief.hpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
)";
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -29,6 +29,7 @@ enum class BuiltIn {
|
||||
HeatmapShader,
|
||||
HeatmapTextureShader,
|
||||
HillshadePrepareShader,
|
||||
ColorReliefShader,
|
||||
HillshadeShader,
|
||||
LineShader,
|
||||
LineGradientShader,
|
||||
|
||||
167
include/mbgl/shaders/vulkan/color_relief.hpp
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
)";
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
163
include/mbgl/shaders/webgpu/color_relief.hpp
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
)";
|
||||
};
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -66,7 +66,8 @@ public:
|
||||
ReadOnlyStorageBuffer,
|
||||
StorageBuffer,
|
||||
Sampler,
|
||||
Texture
|
||||
Texture,
|
||||
UnfilterableTexture // For RGBA32Float textures that require textureLoad instead of textureSample
|
||||
};
|
||||
|
||||
struct BindingInfo {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
59
include/mbgl/style/layers/color_relief_layer.hpp
Normal 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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
After Width: | Height: | Size: 152 KiB |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 51 KiB |
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 70 KiB |
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 84 KiB |
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 95 KiB |
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 88 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 99 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 162 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 139 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 26 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 32 KiB |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 31 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 50 KiB |
@@ -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]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 32 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 81 KiB |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 53 KiB |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 169 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 144 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 142 KiB |
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 158 KiB |
@@ -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]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 84 KiB |
@@ -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]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
metrics/integration/tiles/0-0-0.terrain.png
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
metrics/integration/tiles/1-0-0.terrain.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
metrics/integration/tiles/1-1-0.terrain.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
metrics/integration/tiles/2-1-0.terrain.png
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
metrics/integration/tiles/2-1-1.terrain.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
metrics/integration/tiles/2-2-0.terrain.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
metrics/integration/tiles/2-2-1.terrain.png
Normal file
|
After Width: | Height: | Size: 329 KiB |
2500
package-lock.json
generated
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||