Files
MapLibreParticleLab/include/mln/shaders/vulkan/circle.hpp
Bart Louwers 2a8ebc4906 core: rename mbgl directories to mln (#4511)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-08-22 20:50:14 +02:00

311 lines
8.5 KiB
C++

#pragma once
#include <mln/shaders/shader_source.hpp>
#include <mln/shaders/vulkan/shader_program.hpp>
namespace mln {
namespace shaders {
constexpr auto circleShaderPrelude = R"(
#define idCircleDrawableUBO idDrawableReservedVertexOnlyUBO
#define idCircleEvaluatedPropsUBO layerUBOStartId
)";
template <>
struct ShaderSource<BuiltIn::CircleShader, gfx::Backend::Type::Vulkan> {
static constexpr const char* name = "CircleShader";
static const std::array<AttributeInfo, 8> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto prelude = circleShaderPrelude;
static constexpr auto vertex = R"(
layout(location = 0) in ivec2 in_position;
#if !defined(HAS_UNIFORM_u_color)
layout(location = 1) in vec4 in_color;
#endif
#if !defined(HAS_UNIFORM_u_radius)
layout(location = 2) in vec2 in_radius;
#endif
#if !defined(HAS_UNIFORM_u_blur)
layout(location = 3) in vec2 in_blur;
#endif
#if !defined(HAS_UNIFORM_u_opacity)
layout(location = 4) in vec2 in_opacity;
#endif
#if !defined(HAS_UNIFORM_u_stroke_color)
layout(location = 5) in vec4 in_stroke_color;
#endif
#if !defined(HAS_UNIFORM_u_stroke_width)
layout(location = 6) in vec2 in_stroke_width;
#endif
#if !defined(HAS_UNIFORM_u_stroke_opacity)
layout(location = 7) in vec2 in_stroke_opacity;
#endif
layout(push_constant) uniform Constants {
int ubo_index;
} constant;
struct CircleDrawableUBO {
mat4 matrix;
vec2 extrude_scale;
// Interpolations
float color_t;
float radius_t;
float blur_t;
float opacity_t;
float stroke_color_t;
float stroke_width_t;
float stroke_opacity_t;
float pad1;
float pad2;
float pad3;
};
layout(std140, set = LAYER_SET_INDEX, binding = idCircleDrawableUBO) readonly buffer CircleDrawableUBOVector {
CircleDrawableUBO drawable_ubo[];
} drawableVector;
layout(set = LAYER_SET_INDEX, binding = idCircleEvaluatedPropsUBO) uniform CircleEvaluatedPropsUBO {
vec4 color;
vec4 stroke_color;
float radius;
float blur;
float opacity;
float stroke_width;
float stroke_opacity;
bool scale_with_map;
bool pitch_with_map;
float pad1;
} props;
layout(location = 0) out vec2 frag_extrude;
layout(location = 1) out float frag_antialiasblur;
#if !defined(HAS_UNIFORM_u_color)
layout(location = 2) out vec4 frag_color;
#endif
#if !defined(HAS_UNIFORM_u_radius)
layout(location = 3) out mediump float frag_radius;
#endif
#if !defined(HAS_UNIFORM_u_blur)
layout(location = 4) out lowp float frag_blur;
#endif
#if !defined(HAS_UNIFORM_u_opacity)
layout(location = 5) out lowp float frag_opacity;
#endif
#if !defined(HAS_UNIFORM_u_stroke_color)
layout(location = 6) out vec4 frag_stroke_color;
#endif
#if !defined(HAS_UNIFORM_u_stroke_width)
layout(location = 7) out mediump float frag_stroke_width;
#endif
#if !defined(HAS_UNIFORM_u_stroke_opacity)
layout(location = 8) out lowp float frag_stroke_opacity;
#endif
void main() {
const CircleDrawableUBO drawable = drawableVector.drawable_ubo[constant.ubo_index];
#if defined(HAS_UNIFORM_u_radius)
const float radius = props.radius;
#else
const float radius = unpack_mix_float(in_radius, drawable.radius_t);
#endif
#if defined(HAS_UNIFORM_u_stroke_width)
const float stroke_width = props.stroke_width;
#else
const float stroke_width = unpack_mix_float(in_stroke_width, drawable.stroke_width_t);
#endif
// unencode the extrusion vector that we snuck into the a_pos vector
const vec2 extrude = mod(in_position, 2.0) * 2.0 - 1.0;
const vec2 scaled_extrude = extrude * drawable.extrude_scale;
// multiply a_pos by 0.5, since we had it * 2 in order to sneak in extrusion data
const vec2 circle_center = floor(in_position * 0.5);
if (props.pitch_with_map) {
vec2 corner_position = circle_center;
if (props.scale_with_map) {
corner_position += scaled_extrude * (radius + stroke_width);
} else {
// Pitching the circle with the map effectively scales it with the map
// To counteract the effect for pitch-scale: viewport, we rescale the
// whole circle based on the pitch scaling effect at its central point
const vec4 projected_center = drawable.matrix * vec4(circle_center, 0, 1);
corner_position += scaled_extrude * (radius + stroke_width) *
(projected_center.w / paintParams.camera_to_center_distance);
}
gl_Position = drawable.matrix * vec4(corner_position, 0, 1);
} else {
gl_Position = drawable.matrix * vec4(circle_center, 0, 1);
const float factor = props.scale_with_map ? paintParams.camera_to_center_distance : gl_Position.w;
gl_Position.xy += scaled_extrude * (radius + stroke_width) * factor;
}
applySurfaceTransform();
// This is a minimum blur distance that serves as a faux-antialiasing for
// the circle. since blur is a ratio of the circle's size and the intent is
// to keep the blur at roughly 1px, the two are inversely related.
frag_antialiasblur = 1.0 / DEVICE_PIXEL_RATIO / (radius + stroke_width);
frag_extrude = extrude;
#if !defined(HAS_UNIFORM_u_color)
frag_color = unpack_mix_color(in_color, drawable.color_t);
#endif
#if !defined(HAS_UNIFORM_u_radius)
frag_radius = radius;
#endif
#if !defined(HAS_UNIFORM_u_blur)
frag_blur = unpack_mix_float(in_blur, drawable.blur_t);
#endif
#if !defined(HAS_UNIFORM_u_opacity)
frag_opacity = unpack_mix_float(in_opacity, drawable.opacity_t);
#endif
#if !defined(HAS_UNIFORM_u_stroke_color)
frag_stroke_color = unpack_mix_color(in_stroke_color, drawable.stroke_color_t);
#endif
#if !defined(HAS_UNIFORM_u_stroke_width)
frag_stroke_width = stroke_width;
#endif
#if !defined(HAS_UNIFORM_u_stroke_opacity)
frag_stroke_opacity = unpack_mix_float(in_stroke_opacity, drawable.stroke_opacity_t);
#endif
}
)";
static constexpr auto fragment = R"(
layout(location = 0) in vec2 frag_extrude;
layout(location = 1) in float frag_antialiasblur;
#if !defined(HAS_UNIFORM_u_color)
layout(location = 2) in vec4 frag_color;
#endif
#if !defined(HAS_UNIFORM_u_radius)
layout(location = 3) in mediump float frag_radius;
#endif
#if !defined(HAS_UNIFORM_u_blur)
layout(location = 4) in lowp float frag_blur;
#endif
#if !defined(HAS_UNIFORM_u_opacity)
layout(location = 5) in lowp float frag_opacity;
#endif
#if !defined(HAS_UNIFORM_u_stroke_color)
layout(location = 6) in vec4 frag_stroke_color;
#endif
#if !defined(HAS_UNIFORM_u_stroke_width)
layout(location = 7) in mediump float frag_stroke_width;
#endif
#if !defined(HAS_UNIFORM_u_stroke_opacity)
layout(location = 8) in lowp float frag_stroke_opacity;
#endif
layout(location = 0) out vec4 out_color;
layout(set = LAYER_SET_INDEX, binding = idCircleEvaluatedPropsUBO) uniform CircleEvaluatedPropsUBO {
vec4 color;
vec4 stroke_color;
float radius;
float blur;
float opacity;
float stroke_width;
float stroke_opacity;
bool scale_with_map;
bool pitch_with_map;
float pad1;
} props;
void main() {
#if defined(OVERDRAW_INSPECTOR)
out_color = vec4(1.0);
return;
#endif
#if defined(HAS_UNIFORM_u_color)
const vec4 color = props.color;
#else
const vec4 color = frag_color;
#endif
#if defined(HAS_UNIFORM_u_radius)
const float radius = props.radius;
#else
const float radius = frag_radius;
#endif
#if defined(HAS_UNIFORM_u_blur)
const float blur = props.blur;
#else
const float blur = frag_blur;
#endif
#if defined(HAS_UNIFORM_u_opacity)
const float opacity = props.opacity;
#else
const float opacity = frag_opacity;
#endif
#if defined(HAS_UNIFORM_u_stroke_color)
const vec4 stroke_color = props.stroke_color;
#else
const vec4 stroke_color = frag_stroke_color;
#endif
#if defined(HAS_UNIFORM_u_stroke_width)
const float stroke_width = props.stroke_width;
#else
const float stroke_width = frag_stroke_width;
#endif
#if defined(HAS_UNIFORM_u_stroke_opacity)
const float stroke_opacity = props.stroke_opacity;
#else
const float stroke_opacity = frag_stroke_opacity;
#endif
const float extrude_length = length(frag_extrude);
const float antialiased_blur = -max(blur, frag_antialiasblur);
const float opacity_t = smoothstep(0.0, antialiased_blur, extrude_length - 1.0);
const float color_t = (stroke_width < 0.01) ? 0.0 :
smoothstep(antialiased_blur, 0.0, extrude_length - radius / (radius + stroke_width));
out_color = opacity_t * mix(color * opacity, stroke_color * stroke_opacity, color_t);
}
)";
};
} // namespace shaders
} // namespace mln