Add fill extrusion style property that enables rounded corners for extruded buildings (Core feature) (#4343)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
Alex Cristici
2026-07-21 17:56:36 +03:00
committed by GitHub
parent 7426c42e64
commit d866b289cc
31 changed files with 579 additions and 291 deletions

View File

@@ -9,7 +9,8 @@ template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::OpenGL> {
static constexpr const char* name = "FillExtrusionShader";
static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos;
layout (location = 1) in vec4 a_normal_ed;
layout (location = 1) in vec2 a_decimals_ed;
layout (location = 2) in vec2 a_normal2d;
out vec4 v_color;
layout (std140) uniform FillExtrusionDrawableUBO {
@@ -52,13 +53,13 @@ layout (std140) uniform FillExtrusionPropsUBO {
};
#ifndef HAS_UNIFORM_u_base
layout (location = 2) in highp vec2 a_base;
layout (location = 3) in highp vec2 a_base;
#endif
#ifndef HAS_UNIFORM_u_height
layout (location = 3) in highp vec2 a_height;
layout (location = 4) in highp vec2 a_height;
#endif
#ifndef HAS_UNIFORM_u_color
layout (location = 4) in highp vec4 a_color;
layout (location = 5) in highp vec4 a_color;
#endif
void main() {
@@ -78,14 +79,16 @@ highp vec4 color = unpack_mix_color(a_color, u_color_t);
highp vec4 color = u_color;
#endif
vec3 normal = a_normal_ed.xyz;
vec3 normal = vec3(a_normal2d / 16384.0, a_normal2d.x == 0.0 && a_normal2d.y == 0.0 ? 1.0 : 0.0);
base = max(0.0, base);
height = max(0.0, height);
float t = mod(normal.x, 2.0);
float t = mod(a_decimals_ed.x, 2.0);
float z = t > 0.0 ? height : base;
vec2 decimals = unpack_float(floor(a_decimals_ed.x / 2.0)) / 128.0;
gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1);
gl_Position = u_matrix * vec4(a_pos + decimals, z, 1);
// Relative luminance (how dark/bright is the surface color?)
float colorvalue = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
@@ -97,7 +100,7 @@ highp vec4 color = u_color;
color += ambientlight;
// Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray
float directional = clamp(dot(normal / 16384.0, u_lightpos), 0.0, 1.0);
float directional = clamp(dot(normal, u_lightpos), 0.0, 1.0);
// Adjust directional so that
// the range of values for highlight/shading is narrower
@@ -106,12 +109,12 @@ highp vec4 color = u_color;
directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional);
// Add gradient along z axis of side surfaces
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient,
// and otherwise calculates the gradient based on base + height
directional *= (
(1.0 - u_vertical_gradient) +
(u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0)));
float fMin = mix(0.7, 0.98, 1.0 - u_lightintensity);
float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0);
directional *= (1.0 - u_vertical_gradient) + (u_vertical_gradient * factor);
}
// Assign final color based on surface + ambient light color, diffuse light directional, and light color

View File

@@ -9,7 +9,8 @@ template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::OpenGL> {
static constexpr const char* name = "FillExtrusionPatternShader";
static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos;
layout (location = 1) in vec4 a_normal_ed;
layout (location = 1) in vec2 a_decimals_ed;
layout (location = 2) in vec2 a_normal2d;
out vec2 v_pos_a;
out vec2 v_pos_b;
@@ -67,19 +68,19 @@ layout (std140) uniform FillExtrusionPropsUBO {
};
#ifndef HAS_UNIFORM_u_base
layout (location = 2) in lowp vec2 a_base;
layout (location = 3) in lowp vec2 a_base;
out lowp float base;
#endif
#ifndef HAS_UNIFORM_u_height
layout (location = 3) in lowp vec2 a_height;
layout (location = 4) in lowp vec2 a_height;
out lowp float height;
#endif
#ifndef HAS_UNIFORM_u_pattern_from
layout (location = 4) in mediump vec4 a_pattern_from;
layout (location = 5) in mediump vec4 a_pattern_from;
out mediump vec4 pattern_from;
#endif
#ifndef HAS_UNIFORM_u_pattern_to
layout (location = 5) in mediump vec4 a_pattern_to;
layout (location = 6) in mediump vec4 a_pattern_to;
out mediump vec4 pattern_to;
#endif
@@ -115,8 +116,8 @@ mediump vec4 pattern_to = u_pattern_to;
float fromScale = u_from_scale;
float toScale = u_to_scale;
vec3 normal = a_normal_ed.xyz;
float edgedistance = a_normal_ed.w;
vec3 normal = vec3(a_normal2d / 16384.0, a_normal2d.x == 0.0 && a_normal2d.y == 0.0 ? 1.0 : 0.0);
float edgedistance = a_decimals_ed.y;
vec2 display_size_a = vec2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio);
vec2 display_size_b = vec2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio);
@@ -124,28 +125,28 @@ mediump vec4 pattern_to = u_pattern_to;
base = max(0.0, base);
height = max(0.0, height);
float t = mod(normal.x, 2.0);
float t = mod(a_decimals_ed.x, 2.0);
float z = t > 0.0 ? height : base;
vec2 decimals = unpack_float(floor(a_decimals_ed.x / 2.0)) / 128.0;
gl_Position = u_matrix * vec4(a_pos, z, 1);
gl_Position = u_matrix * vec4(a_pos + decimals, z, 1);
vec2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0
? a_pos // extrusion top
: vec2(edgedistance, z * u_height_factor); // extrusion side
// extrusion top or side
vec2 pos = normal.z == 1.0 ? a_pos : vec2(edgedistance, z * u_height_factor);
v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, fromScale * display_size_a, tileRatio, pos);
v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, toScale * display_size_b, tileRatio, pos);
v_lighting = vec4(0.0, 0.0, 0.0, 1.0);
float directional = clamp(dot(normal / 16383.0, u_lightpos), 0.0, 1.0);
float directional = clamp(dot(normal, u_lightpos), 0.0, 1.0);
directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional);
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient,
// and otherwise calculates the gradient based on base + height
directional *= (
(1.0 - u_vertical_gradient) +
(u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0)));
float fMin = mix(0.7, 0.98, 1.0 - u_lightintensity);
float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0);
directional *= (1.0 - u_vertical_gradient) + (u_vertical_gradient * factor);
}
v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0));

View File

@@ -69,7 +69,7 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal> {
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 4> attributes;
static const std::array<AttributeInfo, 5> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
@@ -78,15 +78,16 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal> {
struct VertexStage {
short2 pos [[attribute(0)]];
ushort2 decimals_ed [[attribute(1)]];
#if !defined(HAS_UNIFORM_u_color)
float4 color [[attribute(1)]];
float4 color [[attribute(2)]];
#endif
#if !defined(HAS_UNIFORM_u_base)
float base [[attribute(2)]];
float base [[attribute(3)]];
#endif
#if !defined(HAS_UNIFORM_u_height)
float height [[attribute(3)]];
float height [[attribute(4)]];
#endif
};
@@ -121,7 +122,9 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const float3 normal = float3(0.0, 0.0, 1.0);
const float t = 1.0;
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(vertx.pos), z, 1);
const float2 decimals = unpack_float(float(vertx.decimals_ed.x / 2)) / 128.0;
const float4 position = drawable.matrix * float4(float2(vertx.pos) + decimals, z, 1);
#if defined(OVERDRAW_INSPECTOR)
return {
@@ -154,7 +157,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
float directional = mix(minDirectional, maxDirectional, directionalFraction);
// Add gradient along z axis of side surfaces
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no
// vertical gradient, and otherwise calculates the gradient based on base + height
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
@@ -212,7 +215,7 @@ struct VertexStage {
struct OutlineInstance {
short2 pos;
ushort2 ed_discard;
ushort2 decimals_ed;
};
struct FragmentStage {
@@ -232,7 +235,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
uint instanceID [[ instance_id ]],
device const OutlineInstance* outline [[buffer(fillExtrusionUBOCount + 1)]]) {
if (outline[instanceID].ed_discard.y) {
bool isDiscarded = glMod(float(outline[instanceID].decimals_ed.x), 2.0) > 0.0;
if (isDiscarded) {
return {
.position = float4(0.0),
.color = half4(0.0),
@@ -252,18 +256,15 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0);
#endif
float2 p1 = float2(outline[instanceID + 1].pos);
float2 p2 = float2(outline[instanceID + 0].pos);
float2 perp = p1 - p2;
float magnitude = sqrt(perp.x * perp.x + perp.y * perp.y);
if (magnitude > 0) {
perp = perp / magnitude;
}
const float2 p1 = float2(outline[instanceID + 0].pos) + unpack_float(float(outline[instanceID + 0].decimals_ed.x / 2)) / 128.0;
const float2 p2 = float2(outline[instanceID + 1].pos) + unpack_float(float(outline[instanceID + 1].decimals_ed.x / 2)) / 128.0;
const float2 edgevector = normalize(p2 - p1);
const float3 normal = float3(-perp.y, perp.x, 0.0);
const float3 normal = float3(-edgevector.y, edgevector.x, 0.0);
const float t = float(vertx.pos.y);
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(outline[instanceID + vertx.pos.x].pos), z, 1);
const float4 position = drawable.matrix * float4(vertx.pos.x == 0.0 ? p1 : p2, z, 1);
#if defined(OVERDRAW_INSPECTOR)
return {
@@ -296,7 +297,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
float directional = mix(minDirectional, maxDirectional, directionalFraction);
// Add gradient along z axis of side surfaces
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no
// vertical gradient, and otherwise calculates the gradient based on base + height
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
@@ -331,7 +332,7 @@ struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Met
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 5> attributes;
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
@@ -340,18 +341,19 @@ struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Met
struct VertexStage {
short2 pos [[attribute(0)]];
ushort2 decimals_ed [[attribute(1)]];
#if !defined(HAS_UNIFORM_u_base)
float base [[attribute(1)]];
float base [[attribute(2)]];
#endif
#if !defined(HAS_UNIFORM_u_height)
float height [[attribute(2)]];
float height [[attribute(3)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_from)
ushort4 pattern_from [[attribute(3)]];
ushort4 pattern_from [[attribute(4)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
ushort4 pattern_to [[attribute(4)]];
ushort4 pattern_to [[attribute(5)]];
#endif
};
@@ -398,7 +400,9 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const float3 normal = float3(0.0, 0.0, 1.0);
const float t = 1.0;
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(vertx.pos), z, 1);
const float2 decimals = unpack_float(float(vertx.decimals_ed.x / 2)) / 128.0;
const float4 position = drawable.matrix * float4(float2(vertx.pos) + decimals, z, 1);
#if defined(OVERDRAW_INSPECTOR)
return {
@@ -441,12 +445,14 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional);
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient,
// and otherwise calculates the gradient based on base + height
directional *= (
(1.0 - props.vertical_gradient) +
(props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0)));
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
// the square root when height is a uniform.
const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor);
}
lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(float3(0.0), float3(0.3), 1.0 - props.light_color_pad.rgb), float3(1.0));
@@ -539,7 +545,7 @@ struct VertexStage {
struct OutlineInstance {
short2 pos;
ushort2 ed_discard;
ushort2 decimals_ed;
};
struct FragmentStage {
@@ -570,7 +576,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
uint instanceID [[ instance_id ]],
device const OutlineInstance* outline [[buffer(fillExtrusionUBOCount + 1)]]) {
if (outline[instanceID].ed_discard.y) {
bool isDiscarded = glMod(float(outline[instanceID].decimals_ed.x), 2.0) > 0.0;
if (isDiscarded) {
return {
.position = float4(0.0),
.lighting = float4(0.0),
@@ -600,19 +607,16 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0);
#endif
float2 p1 = float2(outline[instanceID + 1].pos);
float2 p2 = float2(outline[instanceID + 0].pos);
float2 perp = p1 - p2;
float magnitude = sqrt(perp.x * perp.x + perp.y * perp.y);
if (magnitude > 0) {
perp = perp / magnitude;
}
const float2 p1 = float2(outline[instanceID + 0].pos) + unpack_float(float(outline[instanceID + 0].decimals_ed.x / 2)) / 128.0;
const float2 p2 = float2(outline[instanceID + 1].pos) + unpack_float(float(outline[instanceID + 1].decimals_ed.x / 2)) / 128.0;
const float2 edgevector = normalize(p2 - p1);
const float3 normal = float3(-perp.y, perp.x, 0.0);
const float edgedistance = outline[instanceID + 1 - vertx.pos.x].ed_discard.x;
const float3 normal = float3(-edgevector.y, edgevector.x, 0.0);
const float edgedistance = outline[instanceID + 1 - vertx.pos.x].decimals_ed.y;
const float t = float(vertx.pos.y);
const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it...
const float4 position = drawable.matrix * float4(float2(outline[instanceID + vertx.pos.x].pos), z, 1);
const float4 position = drawable.matrix * float4(vertx.pos.x == 0.0 ? p1 : p2, z, 1);
#if defined(OVERDRAW_INSPECTOR)
return {
@@ -660,12 +664,14 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]],
float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional);
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient,
// and otherwise calculates the gradient based on base + height
directional *= (
(1.0 - props.vertical_gradient) +
(props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0)));
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
// the square root when height is a uniform.
const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor);
}
lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(float3(0.0), float3(0.3), 1.0 - props.light_color_pad.rgb), float3(1.0));

View File

@@ -450,12 +450,12 @@ enum {
enum {
idFillExtrusionPosVertexAttribute,
idFillExtrusionDecimalsEdAttribute,
#if MLN_USE_FILL_EXTRUSION_INSTANCING
idFillExtrusionOutlinePosAttribute,
idFillExtrusionEdDiscardAttribute,
#else
idFillExtrusionNormalEdVertexAttribute,
idFillExtrusionNormal2DVertexAttribute,
#endif
// Data driven

View File

@@ -19,7 +19,7 @@ template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Vulkan> {
static constexpr const char* name = "FillExtrusionShader";
static const std::array<AttributeInfo, 4> attributes;
static const std::array<AttributeInfo, 5> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
@@ -27,17 +27,18 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Vulkan> {
static constexpr auto vertex = R"(
layout(location = 0) in ivec2 in_position;
layout(location = 1) in uvec2 in_decimals_ed;
#if !defined(HAS_UNIFORM_u_color)
layout(location = 1) in vec4 in_color;
layout(location = 2) in vec4 in_color;
#endif
#if !defined(HAS_UNIFORM_u_base)
layout(location = 2) in vec2 in_base;
layout(location = 3) in vec2 in_base;
#endif
#if !defined(HAS_UNIFORM_u_height)
layout(location = 3) in vec2 in_height;
layout(location = 4) in vec2 in_height;
#endif
layout(push_constant) uniform Constants {
@@ -102,9 +103,10 @@ void main() {
const vec3 normal = vec3(0.0, 0.0, 1.0);
const float t = 1.0;
const float z = t != 0.0 ? height : base;
const float z = t > 0.0 ? height : base;
const vec2 decimals = unpack_float(floor(in_decimals_ed.x / 2)) / 128.0;
gl_Position = drawable.matrix * vec4(in_position, z, 1.0);
gl_Position = drawable.matrix * vec4(in_position + decimals, z, 1.0);
applySurfaceTransform();
#if defined(OVERDRAW_INSPECTOR)
@@ -130,7 +132,7 @@ void main() {
float directional = mix(minDirectional, maxDirectional, directionalFraction);
// Add gradient along z axis of side surfaces
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no
// vertical gradient, and otherwise calculates the gradient based on base + height
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
@@ -226,7 +228,7 @@ layout(set = LAYER_SET_INDEX, binding = idFillExtrusionPropsUBO) uniform FillExt
struct OutlineInstance {
int pos;
uint ed_discard;
uint decimals_ed;
};
layout(std430, set = DRAWABLE_UBO_SET_INDEX, binding = idFillExtrusionInstancedDrawableUBO) readonly buffer FillExtrusionInstanceVector {
@@ -238,9 +240,10 @@ layout(location = 0) out mediump vec4 frag_color;
void main() {
const vec2 instancePos = unpack_int(instanceVector.instance[gl_InstanceIndex].pos);
const vec2 instanceEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].ed_discard);
const vec2 instanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].decimals_ed);
if (instanceEd.y > 0.0) {
bool isDiscarded = mod(instanceDecimalsEd.x, 2.0) > 0.0;
if (isDiscarded) {
gl_Position = vec4(0.0);
frag_color = vec4(0.0);
return;
@@ -265,16 +268,18 @@ void main() {
#else
vec4 color = unpack_mix_color(in_color, drawable.color_t);
#endif
const vec2 nextInstancePos = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos);
const vec2 nextInstanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex + 1].decimals_ed);
const vec2 p1 = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos);
const vec2 p2 = unpack_int(instanceVector.instance[gl_InstanceIndex + 0].pos);
const vec2 perp = normalize(p1 - p2);
const vec2 p1 = instancePos + unpack_float(floor(instanceDecimalsEd.x / 2)) / 128.0;
const vec2 p2 = nextInstancePos + unpack_float(floor(nextInstanceDecimalsEd.x / 2)) / 128.0;
const vec2 edgevector = normalize(p2 - p1);
const vec3 normal = vec3(-perp.y, perp.x, 0.0);
const vec3 normal = vec3(-edgevector.y, edgevector.x, 0.0);
const float t = float(in_position.y);
const float z = t != 0.0 ? height : base;
const float z = t > 0.0 ? height : base;
gl_Position = drawable.matrix * vec4(unpack_int(instanceVector.instance[gl_InstanceIndex + in_position.x].pos), z, 1.0);
gl_Position = drawable.matrix * vec4(in_position.x == 0.0 ? p1 : p2, z, 1.0);
applySurfaceTransform();
#if defined(OVERDRAW_INSPECTOR)
@@ -300,7 +305,7 @@ void main() {
float directional = mix(minDirectional, maxDirectional, directionalFraction);
// Add gradient along z axis of side surfaces
if (normal.y != 0.0) {
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no
// vertical gradient, and otherwise calculates the gradient based on base + height
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
@@ -336,7 +341,7 @@ template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Vulkan> {
static constexpr const char* name = "FillExtrusionPatternShader";
static const std::array<AttributeInfo, 5> attributes;
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
@@ -344,21 +349,22 @@ struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Vul
static constexpr auto vertex = R"(
layout(location = 0) in ivec2 in_position;
layout(location = 1) in uvec2 in_decimals_ed;
#if !defined(HAS_UNIFORM_u_base)
layout(location = 1) in vec2 in_base;
layout(location = 2) in vec2 in_base;
#endif
#if !defined(HAS_UNIFORM_u_height)
layout(location = 2) in vec2 in_height;
layout(location = 3) in vec2 in_height;
#endif
#if !defined(HAS_UNIFORM_u_pattern_from)
layout(location = 3) in uvec4 in_pattern_from;
layout(location = 4) in uvec4 in_pattern_from;
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
layout(location = 4) in uvec4 in_pattern_to;
layout(location = 5) in uvec4 in_pattern_to;
#endif
layout(push_constant) uniform Constants {
@@ -440,9 +446,10 @@ void main() {
const vec3 normal = vec3(0.0, 0.0, 1.0);
const float t = 1.0;
const float z = t != 0.0 ? height : base;
const float z = t > 0.0 ? height : base;
const vec2 decimals = unpack_float(floor(in_decimals_ed.x / 2)) / 128.0;
gl_Position = drawable.matrix * vec4(in_position, z, 1.0);
gl_Position = drawable.matrix * vec4(in_position + decimals, z, 1.0);
applySurfaceTransform();
#if defined(OVERDRAW_INSPECTOR)
@@ -487,13 +494,14 @@ void main() {
float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional);
if (normal.y != 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient,
// and otherwise calculates the gradient based on base + height
directional *= (
(1.0 - props.vertical_gradient) +
(props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0)));
}
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no
// vertical gradient, and otherwise calculates the gradient based on base + height
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
// the square root when height is a uniform.
const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor); }
lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(vec3(0.0), vec3(0.3), 1.0 - props.light_color_pad.rgb), vec3(1.0));
lighting *= props.opacity;
@@ -652,7 +660,7 @@ layout(std140, set = LAYER_SET_INDEX, binding = idFillExtrusionDrawableUBO) read
struct OutlineInstance {
int pos;
uint ed_discard;
uint decimals_ed;
};
layout(std430, set = DRAWABLE_UBO_SET_INDEX, binding = idFillExtrusionInstancedDrawableUBO) readonly buffer FillExtrusionInstanceVector {
@@ -699,9 +707,10 @@ layout(location = 4) out mediump vec4 frag_pattern_to;
void main() {
const vec2 instancePos = unpack_int(instanceVector.instance[gl_InstanceIndex].pos);
const vec2 instanceEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].ed_discard);
const vec2 instanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].decimals_ed);
if (instanceEd.y > 0.0) {
bool isDiscarded = mod(instanceDecimalsEd.x, 2.0) > 0.0;
if (isDiscarded) {
gl_Position = vec4(0.0);
return;
}
@@ -720,17 +729,19 @@ void main() {
#else
const float height = max(unpack_mix_float(in_height, drawable.height_t), 0.0);
#endif
const vec2 nextInstancePos = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos);
const vec2 nextInstanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex + 1].decimals_ed);
const vec2 p1 = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos);
const vec2 p2 = unpack_int(instanceVector.instance[gl_InstanceIndex + 0].pos);
const vec2 perp = normalize(p1 - p2);
const vec2 p1 = instancePos + unpack_float(floor(instanceDecimalsEd.x / 2)) / 128.0;
const vec2 p2 = nextInstancePos + unpack_float(floor(nextInstanceDecimalsEd.x / 2)) / 128.0;
const vec2 edgevector = normalize(p2 - p1);
const vec3 normal = vec3(-perp.y, perp.x, 0.0);
const float edgedistance = unpack_uint(instanceVector.instance[gl_InstanceIndex + 1 - in_position.x].ed_discard).x;
const vec3 normal = vec3(-edgevector.y, edgevector.x, 0.0);
const float edgedistance = in_position.x == 0.0 ? nextInstanceDecimalsEd.y : instanceDecimalsEd.y;
const float t = float(in_position.y);
const float z = t != 0.0 ? height : base;
const float z = t > 0.0 ? height : base;
gl_Position = drawable.matrix * vec4(unpack_int(instanceVector.instance[gl_InstanceIndex + in_position.x].pos), z, 1.0);
gl_Position = drawable.matrix * vec4(in_position.x == 0.0 ? p1 : p2, z, 1.0);
applySurfaceTransform();
#if defined(OVERDRAW_INSPECTOR)
@@ -775,12 +786,14 @@ void main() {
float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional);
if (normal.y != 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient,
// and otherwise calculates the gradient based on base + height
directional *= (
(1.0 - props.vertical_gradient) +
(props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0)));
if (normal.z == 0.0) {
// This avoids another branching statement, but multiplies by a constant of 0.84 if no
// vertical gradient, and otherwise calculates the gradient based on base + height
// TODO: If we're optimizing to the level of avoiding branches, we should pre-compute
// the square root when height is a uniform.
const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor);
}
lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(vec3(0.0), vec3(0.3), 1.0 - props.light_color_pad.rgb), vec3(1.0));

View File

@@ -10,22 +10,23 @@ namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillExtrusionShader";
static const std::array<AttributeInfo, 5> attributes;
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) normal_ed: vec4<i32>,
@location(4) decimals_ed: vec2<u32>,
@location(5) normal2d: vec2<i32>,
#ifndef HAS_UNIFORM_u_color
@location(5) color: vec4<f32>,
@location(6) color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_base
@location(6) base: vec2<f32>,
@location(7) base: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_height
@location(7) height: vec2<f32>,
@location(8) height: vec2<f32>,
#endif
};
@@ -88,11 +89,13 @@ fn main(in: VertexInput) -> VertexOutput {
let heightValue = max(props.height, 0.0);
#endif
let normal_i = vec3<f32>(f32(in.normal_ed.x), f32(in.normal_ed.y), f32(in.normal_ed.z));
let t = glMod(normal_i.x, 2.0);
let z = select(baseValue, heightValue, t != 0.0);
let normal = vec3<f32>(vec2<f32>(in.normal2d) / 16384.0, select(0.0, 1.0, in.normal2d.x == 0 && in.normal2d.y == 0));
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), z, 1.0);
let t = glMod(f32(in.decimals_ed.x), 2.0);
let z = select(baseValue, heightValue, t > 0.0);
let decimals = unpack_float(f32(in.decimals_ed.x / 2)) / 128.0;
out.position = drawable.matrix * vec4<f32>(vec2<f32>(in.position) + decimals, z, 1.0);
#ifdef OVERDRAW_INSPECTOR
out.color = vec4<f32>(1.0, 1.0, 1.0, 1.0);
@@ -107,13 +110,12 @@ fn main(in: VertexInput) -> VertexOutput {
color = color + min(vec4<f32>(0.03, 0.03, 0.03, 1.0), vec4<f32>(1.0));
let luminance = dot(color.rgb, vec3<f32>(0.2126, 0.7152, 0.0722));
let unitNormal = normal_i / 16384.0;
let directionalFraction = clamp(dot(unitNormal, props.light_position_base.xyz), 0.0, 1.0);
let directionalFraction = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
let minDirectional = 1.0 - props.light_intensity;
let maxDirectional = max(1.0 - luminance + props.light_intensity, 1.0);
var directional = mix(minDirectional, maxDirectional, directionalFraction);
if (normal_i.y != 0.0) {
if (normal.z == 0.0) {
let gradientMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
let factor = clamp((t + baseValue) * pow(heightValue / 150.0, 0.5), gradientMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + props.vertical_gradient * factor;
@@ -146,25 +148,26 @@ fn main(in: FragmentInput) -> @location(0) vec4<f32> {
template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillExtrusionPatternShader";
static const std::array<AttributeInfo, 6> attributes;
static const std::array<AttributeInfo, 7> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) normal_ed: vec4<i32>,
@location(4) decimals_ed: vec2<u32>,
@location(5) normal2d: vec2<i32>,
#ifndef HAS_UNIFORM_u_base
@location(5) base: vec2<f32>,
@location(6) base: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_height
@location(6) height: vec2<f32>,
@location(7) height: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_from
@location(7) pattern_from: vec4<u32>,
@location(8) pattern_from: vec4<u32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(8) pattern_to: vec4<u32>,
@location(9) pattern_to: vec4<u32>,
#endif
};
@@ -250,16 +253,18 @@ fn main(in: VertexInput) -> VertexOutput {
let heightValue = max(props.height, 0.0);
#endif
let normal_i = vec3<f32>(f32(in.normal_ed.x), f32(in.normal_ed.y), f32(in.normal_ed.z));
let edgedistance = f32(in.normal_ed.w);
let t = glMod(normal_i.x, 2.0);
let z = select(baseValue, heightValue, t != 0.0);
let normal = vec3<f32>(vec2<f32>(in.normal2d) / 16384.0, select(0.0, 1.0, in.normal2d.x == 0 && in.normal2d.y == 0));
let edgedistance = f32(in.decimals_ed.y);
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), z, 1.0);
let t = glMod(f32(in.decimals_ed.x), 2.0);
let z = select(baseValue, heightValue, t > 0.0);
let decimals = unpack_float(f32(in.decimals_ed.x / 2)) / 128.0;
out.position = drawable.matrix * vec4<f32>(vec2<f32>(in.position) + decimals, z, 1.0);
var patternPos: vec2<f32>;
if (normal_i.x == 1.0 && normal_i.y == 0.0 && normal_i.z == 16384.0) {
patternPos = vec2<f32>(f32(in.position.x), f32(in.position.y));
if (normal.z == 1.0) {
patternPos = vec2<f32>(in.position);
} else {
patternPos = vec2<f32>(edgedistance, z * drawable.height_factor);
}
@@ -294,10 +299,10 @@ fn main(in: VertexInput) -> VertexOutput {
(pattern_br_b.y - pattern_tl_b.y) / pixelRatio);
var lighting = vec4<f32>(0.0, 0.0, 0.0, 1.0);
var directional = clamp(dot(normal_i / 16383.0, props.light_position_base.xyz), 0.0, 1.0);
var directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0);
directional = mix(1.0 - props.light_intensity, max(0.5 + props.light_intensity, 1.0), directional);
if (normal_i.y != 0.0) {
if (normal.z == 0.0) {
let gradientMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
let factor = clamp((t + baseValue) * pow(heightValue / 150.0, 0.5), gradientMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + props.vertical_gradient * factor;

View File

@@ -19,6 +19,12 @@ public:
FillExtrusionLayer(const std::string& layerID, const std::string& sourceID);
~FillExtrusionLayer() override;
// Layout properties
static PropertyValue<float> getDefaultFillExtrusionRoundedCornerDistance();
const PropertyValue<float>& getFillExtrusionRoundedCornerDistance() const;
void setFillExtrusionRoundedCornerDistance(const PropertyValue<float>&);
// Paint properties
static PropertyValue<float> getDefaultFillExtrusionBase();