diff options
author | Molly Lloyd <molly@mapbox.com> | 2017-11-07 17:04:39 -0800 |
---|---|---|
committer | Molly Lloyd <molly@mapbox.com> | 2018-01-18 18:10:08 -0800 |
commit | 7763571375a4975cc1b7623eb342483fd7726de8 (patch) | |
tree | 71b11c57e07abd277e575ff4ca1088e0c164335d | |
parent | b3d3ab0e31d251cf39dfa78dfbc0661b8819a8ab (diff) | |
download | qtlocation-mapboxgl-7763571375a4975cc1b7623eb342483fd7726de8.tar.gz |
add hillshade shaders
-rw-r--r-- | cmake/core-files.cmake | 4 | ||||
-rw-r--r-- | src/mbgl/programs/hillshade_program.hpp | 55 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_hillshade_layer.cpp | 155 | ||||
-rw-r--r-- | src/mbgl/shaders/hillshade.cpp | 70 | ||||
-rw-r--r-- | src/mbgl/shaders/hillshade.hpp | 16 | ||||
-rw-r--r-- | src/mbgl/shaders/hillshade_prepare.cpp | 99 | ||||
-rw-r--r-- | src/mbgl/shaders/hillshade_prepare.hpp | 16 |
7 files changed, 415 insertions, 0 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 8866932fc4..4b56c633cb 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -285,6 +285,10 @@ set(MBGL_CORE_FILES src/mbgl/shaders/fill_outline_pattern.hpp src/mbgl/shaders/fill_pattern.cpp src/mbgl/shaders/fill_pattern.hpp + src/mbgl/shaders/hillshade.cpp + src/mbgl/shaders/hillshade.hpp + src/mbgl/shaders/hillshade_prepare.cpp + src/mbgl/shaders/hillshade_prepare.hpp src/mbgl/shaders/line.cpp src/mbgl/shaders/line.hpp src/mbgl/shaders/line_pattern.cpp diff --git a/src/mbgl/programs/hillshade_program.hpp b/src/mbgl/programs/hillshade_program.hpp new file mode 100644 index 0000000000..065e262be6 --- /dev/null +++ b/src/mbgl/programs/hillshade_program.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include <mbgl/programs/program.hpp> +#include <mbgl/programs/attributes.hpp> +#include <mbgl/programs/uniforms.hpp> +#include <mbgl/shaders/hillshade.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/style/layers/hillshade_layer_properties.hpp> + +namespace mbgl { + +namespace uniforms { +MBGL_DEFINE_UNIFORM_SCALAR(Color, u_shadow); +MBGL_DEFINE_UNIFORM_SCALAR(Color, u_highlight); +MBGL_DEFINE_UNIFORM_SCALAR(Color, u_accent); +MBGL_DEFINE_UNIFORM_VECTOR(float, 3, u_light); +MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_latrange); +} // namespace uniforms + +class HillshadeProgram : public Program< + shaders::hillshade, + gl::Triangle, + gl::Attributes< + attributes::a_pos, + attributes::a_texture_pos>, + gl::Uniforms< + uniforms::u_matrix, + uniforms::u_image, + uniforms::u_highlight, + uniforms::u_shadow, + uniforms::u_accent, + uniforms::u_light, + uniforms::u_latrange>, + style::HillshadePaintProperties>{ +public: + using Program::Program; + + static LayoutVertex layoutVertex(Point<int16_t> p, Point<uint16_t> t) { + return LayoutVertex { + {{ + p.x, + p.y + }}, + {{ + t.x, + t.y + }} + }; + } +}; + +using HillshadeLayoutVertex = HillshadeProgram::LayoutVertex; +using HillshadeAttributes = HillshadeProgram::Attributes; + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp new file mode 100644 index 0000000000..9a23b35ba7 --- /dev/null +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -0,0 +1,155 @@ +#include <mbgl/renderer/layers/render_hillshade_layer.hpp> +#include <mbgl/renderer/buckets/hillshade_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/hillshade_program.hpp> +#include <mbgl/programs/hillshade_prepare_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/hillshade_layer_impl.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/offscreen_texture.hpp> + +namespace mbgl { + +using namespace style; +RenderHillshadeLayer::RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl> _impl) + : RenderLayer(style::LayerType::Hillshade, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::HillshadeLayer::Impl& RenderHillshadeLayer::impl() const { + return static_cast<const style::HillshadeLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderHillshadeLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +const std::array<float, 2> RenderHillshadeLayer::getLatRange(const UnwrappedTileID& id) { + const LatLng latlng0 = LatLng(id); + const LatLng latlng1 = LatLng(UnwrappedTileID(id.canonical.z, id.canonical.x, id.canonical.y + 1)); + return {{ (float)latlng0.latitude(), (float)latlng1.latitude() }}; +} + +const std::array<float, 3> RenderHillshadeLayer::getLight(const PaintParameters& parameters){ + float azimuthal = evaluated.get<HillshadeIlluminationDirection>() * util::DEG2RAD; + const float zenith = 30.0f * util::DEG2RAD; + if (evaluated.get<HillshadeIlluminationAnchor>() == Viewport) azimuthal = azimuthal - parameters.state.getAngle(); + return {{evaluated.get<HillshadeExaggeration>(), azimuthal, zenith}}; +} + +void RenderHillshadeLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderHillshadeLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + passes = RenderPass::Translucent; +} + +bool RenderHillshadeLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass != RenderPass::Translucent) + return; + + auto draw = [&] (const mat4& matrix, + const auto& vertexBuffer, + const auto& indexBuffer, + const auto& segments, + const UnwrappedTileID& id) { + parameters.programs.hillshade.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + HillshadeProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_image::Value{ 0 }, + uniforms::u_highlight::Value{ evaluated.get<HillshadeHighlightColor>() }, + uniforms::u_shadow::Value{ evaluated.get<HillshadeShadowColor>() }, + uniforms::u_accent::Value{ evaluated.get<HillshadeAccentColor>() }, + uniforms::u_light::Value{ getLight(parameters) }, + uniforms::u_latrange::Value{ getLatRange(id) }, + }, + vertexBuffer, + indexBuffer, + segments, + HillshadeProgram::PaintPropertyBinders { evaluated, 0 }, + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<HillshadeBucket*>(tile.tile.getBucket(*baseImpl))); + HillshadeBucket& bucket = *reinterpret_cast<HillshadeBucket*>(tile.tile.getBucket(*baseImpl)); + + + if (!bucket.hasData()) + continue; + if (!bucket.prepared) { + OffscreenTexture view(parameters.context, { 256, 256 }); + view.bind(); + + parameters.context.bindTexture(*bucket.dem, 0, gl::TextureFilter::Linear, gl::TextureMipMap::Yes); + const Properties<>::PossiblyEvaluated properties; + + mat4 mat; + matrix::ortho(mat, 0, util::EXTENT, -util::EXTENT, 0, 0, 1); + matrix::translate(mat, mat, 0, -util::EXTENT, 0); + + parameters.programs.hillshadePrepare.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + HillshadePrepareProgram::UniformValues { + uniforms::u_matrix::Value { mat }, + uniforms::u_dimension::Value { {{512, 512 }} }, + uniforms::u_zoom::Value{ float(tile.id.canonical.z) }, + uniforms::u_image::Value{ 0 } + }, + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments, + HillshadePrepareProgram::PaintPropertyBinders { properties, 0 }, + properties, + parameters.state.getZoom(), + getID() + ); + bucket.texture = std::move(view.getTexture()); + bucket.prepared = true; + } + + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); + + if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) { + // Draw only the parts of the tile that aren't drawn by another tile in the layer. + draw(tile.matrix, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + tile.id); + } else { + // Draw the full tile. + draw(tile.matrix, + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments, + tile.id); + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/shaders/hillshade.cpp b/src/mbgl/shaders/hillshade.cpp new file mode 100644 index 0000000000..16fbe7834c --- /dev/null +++ b/src/mbgl/shaders/hillshade.cpp @@ -0,0 +1,70 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/hillshade.hpp> + +namespace mbgl { +namespace shaders { + +const char* hillshade::name = "hillshade"; +const char* hillshade::vertexSource = R"MBGL_SHADER( +uniform mat4 u_matrix; + +attribute vec2 a_pos; +attribute vec2 a_texture_pos; + +varying vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_pos = a_texture_pos / 8192.0; +} + +)MBGL_SHADER"; +const char* hillshade::fragmentSource = R"MBGL_SHADER( +uniform sampler2D u_image; +varying vec2 v_pos; +uniform vec2 u_latrange; + +uniform int u_mode; +uniform float u_mipmap; + +uniform vec3 u_light; + +uniform vec4 u_shadow; +uniform vec4 u_highlight; +uniform vec4 u_accent; + +#define PI 3.141592653589793 + +void main() { + vec4 pixel = texture2D(u_image, v_pos); + + vec2 deriv = ((pixel.rg * 2.0) - 1.0); + + // we multiply the slope by a scale factor based on the cosin of the pixel's approximate latitude + float scaleFactor = cos(radians((u_latrange[0] - u_latrange[1]) * (1.0 - v_pos.y) + u_latrange[1])); + 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; + float polar = u_light.z; + + float accent = cos(slope); + vec4 accent_color = intensity * clamp((1.0 - accent) * 2.0, 0.0, 1.0) * u_accent; + float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0); + vec4 shade_color = mix(u_shadow, u_highlight, shade) * slope * sin(polar) * mix(0.0, 2.0, intensity); + gl_FragColor = accent_color * (1.0 - shade_color.a) + shade_color; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/hillshade.hpp b/src/mbgl/shaders/hillshade.hpp new file mode 100644 index 0000000000..a4a27cb595 --- /dev/null +++ b/src/mbgl/shaders/hillshade.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class hillshade { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/hillshade_prepare.cpp b/src/mbgl/shaders/hillshade_prepare.cpp new file mode 100644 index 0000000000..733658435e --- /dev/null +++ b/src/mbgl/shaders/hillshade_prepare.cpp @@ -0,0 +1,99 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/hillshade_prepare.hpp> + +namespace mbgl { +namespace shaders { + +const char* hillshade_prepare::name = "hillshade_prepare"; +const char* hillshade_prepare::vertexSource = R"MBGL_SHADER( +uniform mat4 u_matrix; + +attribute vec2 a_pos; +attribute vec2 a_texture_pos; + +varying vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_pos = (a_texture_pos / 8192.0) / 2.0 + 0.25; +} + +)MBGL_SHADER"; +const char* hillshade_prepare::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#endif + +uniform sampler2D u_image; +varying vec2 v_pos; +uniform vec2 u_dimension; +uniform float u_zoom; + +float getElevation(vec2 coord, float bias) { + // Convert encoded elevation value to meters + vec4 data = texture2D(u_image, coord) * 255.0; + return (data.r + data.g * 256.0 + data.b * 256.0 * 256.0) / 4.0; +} + +void main() { + vec2 epsilon = 1.0 / u_dimension; + + // queried pixels: + // +-----------+ + // | | | | + // | a | b | c | + // | | | | + // +-----------+ + // | | | | + // | d | e | f | + // | | | | + // +-----------+ + // | | | | + // | g | h | i | + // | | | | + // +-----------+ + + float a = getElevation(v_pos + vec2(-epsilon.x, -epsilon.y), 0.0); + float b = getElevation(v_pos + vec2(0, -epsilon.y), 0.0); + float c = getElevation(v_pos + vec2(epsilon.x, -epsilon.y), 0.0); + float d = getElevation(v_pos + vec2(-epsilon.x, 0), 0.0); + float e = getElevation(v_pos, 0.0); + float f = getElevation(v_pos + vec2(epsilon.x, 0), 0.0); + float g = getElevation(v_pos + vec2(-epsilon.x, epsilon.y), 0.0); + 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 - 14) * a) where a is an arbitrary value and 14 is the + // maxzoom of the tile source. 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 = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3; + + vec2 deriv = vec2( + (c + f + f + i) - (a + d + d + g), + (g + h + h + i) - (a + b + b + c) + ) / pow(2.0, (u_zoom - 14.0) * exaggeration + 19.2562 - u_zoom); + + gl_FragColor = clamp(vec4( + deriv.x / 2.0 + 0.5, + deriv.y / 2.0 + 0.5, + 1.0, + 1.0), 0.0, 1.0); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/hillshade_prepare.hpp b/src/mbgl/shaders/hillshade_prepare.hpp new file mode 100644 index 0000000000..c38b4a0d19 --- /dev/null +++ b/src/mbgl/shaders/hillshade_prepare.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class hillshade_prepare { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl |