Added draw package as renderer focused on mixed use layout / 2D / 3D scene applications (#7)
Co-authored-by: Zachary Levy <zachary@sunforge.is> Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
296
draw/shaders/generated/base_2d.frag.metal
Normal file
296
draw/shaders/generated/base_2d.frag.metal
Normal file
@@ -0,0 +1,296 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()
|
||||
template<typename Tx, typename Ty>
|
||||
inline Tx mod(Tx x, Ty y)
|
||||
{
|
||||
return x - y * floor(x / y);
|
||||
}
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 out_color [[color(0)]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 f_color [[user(locn0)]];
|
||||
float2 f_local_or_uv [[user(locn1)]];
|
||||
float4 f_params [[user(locn2)]];
|
||||
float4 f_params2 [[user(locn3)]];
|
||||
uint f_kind_flags [[user(locn4)]];
|
||||
float f_rotation [[user(locn5), flat]];
|
||||
};
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float2 apply_rotation(thread const float2& p, thread const float& angle)
|
||||
{
|
||||
float cr = cos(-angle);
|
||||
float sr = sin(-angle);
|
||||
return float2x2(float2(cr, sr), float2(-sr, cr)) * p;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float sdRoundedBox(thread const float2& p, thread const float2& b, thread float4& r)
|
||||
{
|
||||
float2 _61;
|
||||
if (p.x > 0.0)
|
||||
{
|
||||
_61 = r.xy;
|
||||
}
|
||||
else
|
||||
{
|
||||
_61 = r.zw;
|
||||
}
|
||||
r.x = _61.x;
|
||||
r.y = _61.y;
|
||||
float _78;
|
||||
if (p.y > 0.0)
|
||||
{
|
||||
_78 = r.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
_78 = r.y;
|
||||
}
|
||||
r.x = _78;
|
||||
float2 q = (abs(p) - b) + float2(r.x);
|
||||
return (fast::min(fast::max(q.x, q.y), 0.0) + length(fast::max(q, float2(0.0)))) - r.x;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float sdf_stroke(thread const float& d, thread const float& stroke_width)
|
||||
{
|
||||
return abs(d) - (stroke_width * 0.5);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float sdCircle(thread const float2& p, thread const float& r)
|
||||
{
|
||||
return length(p) - r;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float sdEllipse(thread float2& p, thread float2& ab)
|
||||
{
|
||||
p = abs(p);
|
||||
if (p.x > p.y)
|
||||
{
|
||||
p = p.yx;
|
||||
ab = ab.yx;
|
||||
}
|
||||
float l = (ab.y * ab.y) - (ab.x * ab.x);
|
||||
float m = (ab.x * p.x) / l;
|
||||
float m2 = m * m;
|
||||
float n = (ab.y * p.y) / l;
|
||||
float n2 = n * n;
|
||||
float c = ((m2 + n2) - 1.0) / 3.0;
|
||||
float c3 = (c * c) * c;
|
||||
float q = c3 + ((m2 * n2) * 2.0);
|
||||
float d = c3 + (m2 * n2);
|
||||
float g = m + (m * n2);
|
||||
float co;
|
||||
if (d < 0.0)
|
||||
{
|
||||
float h = acos(q / c3) / 3.0;
|
||||
float s = cos(h);
|
||||
float t = sin(h) * 1.73205077648162841796875;
|
||||
float rx = sqrt(((-c) * ((s + t) + 2.0)) + m2);
|
||||
float ry = sqrt(((-c) * ((s - t) + 2.0)) + m2);
|
||||
co = (((ry + (sign(l) * rx)) + (abs(g) / (rx * ry))) - m) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float h_1 = ((2.0 * m) * n) * sqrt(d);
|
||||
float s_1 = sign(q + h_1) * powr(abs(q + h_1), 0.3333333432674407958984375);
|
||||
float u = sign(q - h_1) * powr(abs(q - h_1), 0.3333333432674407958984375);
|
||||
float rx_1 = (((-s_1) - u) - (c * 4.0)) + (2.0 * m2);
|
||||
float ry_1 = (s_1 - u) * 1.73205077648162841796875;
|
||||
float rm = sqrt((rx_1 * rx_1) + (ry_1 * ry_1));
|
||||
co = (((ry_1 / sqrt(rm - rx_1)) + ((2.0 * g) / rm)) - m) / 2.0;
|
||||
}
|
||||
float2 r = ab * float2(co, sqrt(1.0 - (co * co)));
|
||||
return length(r - p) * sign(p.y - r.y);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float sdSegment(thread const float2& p, thread const float2& a, thread const float2& b)
|
||||
{
|
||||
float2 pa = p - a;
|
||||
float2 ba = b - a;
|
||||
float h = fast::clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
||||
return length(pa - (ba * h));
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float sdf_alpha(thread const float& d, thread const float& soft)
|
||||
{
|
||||
return 1.0 - smoothstep(-soft, soft, d);
|
||||
}
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler texSmplr [[sampler(0)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
uint kind = in.f_kind_flags & 255u;
|
||||
uint flags = (in.f_kind_flags >> 8u) & 255u;
|
||||
if (kind == 0u)
|
||||
{
|
||||
out.out_color = in.f_color * tex.sample(texSmplr, in.f_local_or_uv);
|
||||
return out;
|
||||
}
|
||||
float d = 1000000015047466219876688855040.0;
|
||||
float soft = 1.0;
|
||||
if (kind == 1u)
|
||||
{
|
||||
float2 b = in.f_params.xy;
|
||||
float4 r = float4(in.f_params.zw, in.f_params2.xy);
|
||||
soft = fast::max(in.f_params2.z, 1.0);
|
||||
float stroke_px = in.f_params2.w;
|
||||
float2 p_local = in.f_local_or_uv;
|
||||
if (in.f_rotation != 0.0)
|
||||
{
|
||||
float2 param = p_local;
|
||||
float param_1 = in.f_rotation;
|
||||
p_local = apply_rotation(param, param_1);
|
||||
}
|
||||
float2 param_2 = p_local;
|
||||
float2 param_3 = b;
|
||||
float4 param_4 = r;
|
||||
float _491 = sdRoundedBox(param_2, param_3, param_4);
|
||||
d = _491;
|
||||
if ((flags & 1u) != 0u)
|
||||
{
|
||||
float param_5 = d;
|
||||
float param_6 = stroke_px;
|
||||
d = sdf_stroke(param_5, param_6);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (kind == 2u)
|
||||
{
|
||||
float radius = in.f_params.x;
|
||||
soft = fast::max(in.f_params.y, 1.0);
|
||||
float stroke_px_1 = in.f_params.z;
|
||||
float2 param_7 = in.f_local_or_uv;
|
||||
float param_8 = radius;
|
||||
d = sdCircle(param_7, param_8);
|
||||
if ((flags & 1u) != 0u)
|
||||
{
|
||||
float param_9 = d;
|
||||
float param_10 = stroke_px_1;
|
||||
d = sdf_stroke(param_9, param_10);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (kind == 3u)
|
||||
{
|
||||
float2 ab = in.f_params.xy;
|
||||
soft = fast::max(in.f_params.z, 1.0);
|
||||
float stroke_px_2 = in.f_params.w;
|
||||
float2 p_local_1 = in.f_local_or_uv;
|
||||
if (in.f_rotation != 0.0)
|
||||
{
|
||||
float2 param_11 = p_local_1;
|
||||
float param_12 = in.f_rotation;
|
||||
p_local_1 = apply_rotation(param_11, param_12);
|
||||
}
|
||||
float2 param_13 = p_local_1;
|
||||
float2 param_14 = ab;
|
||||
float _560 = sdEllipse(param_13, param_14);
|
||||
d = _560;
|
||||
if ((flags & 1u) != 0u)
|
||||
{
|
||||
float param_15 = d;
|
||||
float param_16 = stroke_px_2;
|
||||
d = sdf_stroke(param_15, param_16);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (kind == 4u)
|
||||
{
|
||||
float2 a = in.f_params.xy;
|
||||
float2 b_1 = in.f_params.zw;
|
||||
float width = in.f_params2.x;
|
||||
soft = fast::max(in.f_params2.y, 1.0);
|
||||
float2 param_17 = in.f_local_or_uv;
|
||||
float2 param_18 = a;
|
||||
float2 param_19 = b_1;
|
||||
d = sdSegment(param_17, param_18, param_19) - (width * 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (kind == 5u)
|
||||
{
|
||||
float inner = in.f_params.x;
|
||||
float outer = in.f_params.y;
|
||||
float start_rad = in.f_params.z;
|
||||
float end_rad = in.f_params.w;
|
||||
soft = fast::max(in.f_params2.x, 1.0);
|
||||
float r_1 = length(in.f_local_or_uv);
|
||||
float d_ring = fast::max(inner - r_1, r_1 - outer);
|
||||
float angle = precise::atan2(in.f_local_or_uv.y, in.f_local_or_uv.x);
|
||||
if (angle < 0.0)
|
||||
{
|
||||
angle += 6.283185482025146484375;
|
||||
}
|
||||
float ang_start = mod(start_rad, 6.283185482025146484375);
|
||||
float ang_end = mod(end_rad, 6.283185482025146484375);
|
||||
float _654;
|
||||
if (ang_end > ang_start)
|
||||
{
|
||||
_654 = float((angle >= ang_start) && (angle <= ang_end));
|
||||
}
|
||||
else
|
||||
{
|
||||
_654 = float((angle >= ang_start) || (angle <= ang_end));
|
||||
}
|
||||
float in_arc = _654;
|
||||
if (abs(ang_end - ang_start) >= 6.282185077667236328125)
|
||||
{
|
||||
in_arc = 1.0;
|
||||
}
|
||||
d = (in_arc > 0.5) ? d_ring : 1000000015047466219876688855040.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (kind == 6u)
|
||||
{
|
||||
float radius_1 = in.f_params.x;
|
||||
float rotation = in.f_params.y;
|
||||
float sides = in.f_params.z;
|
||||
soft = fast::max(in.f_params.w, 1.0);
|
||||
float stroke_px_3 = in.f_params2.x;
|
||||
float2 p = in.f_local_or_uv;
|
||||
float c = cos(rotation);
|
||||
float s = sin(rotation);
|
||||
p = float2x2(float2(c, -s), float2(s, c)) * p;
|
||||
float an = 3.1415927410125732421875 / sides;
|
||||
float bn = mod(precise::atan2(p.y, p.x), 2.0 * an) - an;
|
||||
d = (length(p) * cos(bn)) - radius_1;
|
||||
if ((flags & 1u) != 0u)
|
||||
{
|
||||
float param_20 = d;
|
||||
float param_21 = stroke_px_3;
|
||||
d = sdf_stroke(param_20, param_21);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
float param_22 = d;
|
||||
float param_23 = soft;
|
||||
float alpha = sdf_alpha(param_22, param_23);
|
||||
out.out_color = float4(in.f_color.xyz, in.f_color.w * alpha);
|
||||
return out;
|
||||
}
|
||||
|
||||
BIN
draw/shaders/generated/base_2d.frag.spv
Normal file
BIN
draw/shaders/generated/base_2d.frag.spv
Normal file
Binary file not shown.
94
draw/shaders/generated/base_2d.vert.metal
Normal file
94
draw/shaders/generated/base_2d.vert.metal
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct Uniforms
|
||||
{
|
||||
float4x4 projection;
|
||||
float dpi_scale;
|
||||
uint mode;
|
||||
};
|
||||
|
||||
struct Primitive
|
||||
{
|
||||
float4 bounds;
|
||||
uint color;
|
||||
uint kind_flags;
|
||||
float rotation;
|
||||
float _pad;
|
||||
float4 params;
|
||||
float4 params2;
|
||||
};
|
||||
|
||||
struct Primitive_1
|
||||
{
|
||||
float4 bounds;
|
||||
uint color;
|
||||
uint kind_flags;
|
||||
float rotation;
|
||||
float _pad;
|
||||
float4 params;
|
||||
float4 params2;
|
||||
};
|
||||
|
||||
struct Primitives
|
||||
{
|
||||
Primitive_1 primitives[1];
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 f_color [[user(locn0)]];
|
||||
float2 f_local_or_uv [[user(locn1)]];
|
||||
float4 f_params [[user(locn2)]];
|
||||
float4 f_params2 [[user(locn3)]];
|
||||
uint f_kind_flags [[user(locn4)]];
|
||||
float f_rotation [[user(locn5)]];
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float2 v_position [[attribute(0)]];
|
||||
float2 v_uv [[attribute(1)]];
|
||||
float4 v_color [[attribute(2)]];
|
||||
};
|
||||
|
||||
vertex main0_out main0(main0_in in [[stage_in]], constant Uniforms& _12 [[buffer(0)]], const device Primitives& _72 [[buffer(1)]], uint gl_InstanceIndex [[instance_id]])
|
||||
{
|
||||
main0_out out = {};
|
||||
if (_12.mode == 0u)
|
||||
{
|
||||
out.f_color = in.v_color;
|
||||
out.f_local_or_uv = in.v_uv;
|
||||
out.f_params = float4(0.0);
|
||||
out.f_params2 = float4(0.0);
|
||||
out.f_kind_flags = 0u;
|
||||
out.f_rotation = 0.0;
|
||||
out.gl_Position = _12.projection * float4(in.v_position * _12.dpi_scale, 0.0, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Primitive p;
|
||||
p.bounds = _72.primitives[int(gl_InstanceIndex)].bounds;
|
||||
p.color = _72.primitives[int(gl_InstanceIndex)].color;
|
||||
p.kind_flags = _72.primitives[int(gl_InstanceIndex)].kind_flags;
|
||||
p.rotation = _72.primitives[int(gl_InstanceIndex)].rotation;
|
||||
p._pad = _72.primitives[int(gl_InstanceIndex)]._pad;
|
||||
p.params = _72.primitives[int(gl_InstanceIndex)].params;
|
||||
p.params2 = _72.primitives[int(gl_InstanceIndex)].params2;
|
||||
float2 corner = in.v_position;
|
||||
float2 world_pos = mix(p.bounds.xy, p.bounds.zw, corner);
|
||||
float2 center = (p.bounds.xy + p.bounds.zw) * 0.5;
|
||||
out.f_color = unpack_unorm4x8_to_float(p.color);
|
||||
out.f_local_or_uv = (world_pos - center) * _12.dpi_scale;
|
||||
out.f_params = p.params;
|
||||
out.f_params2 = p.params2;
|
||||
out.f_kind_flags = p.kind_flags;
|
||||
out.f_rotation = p.rotation;
|
||||
out.gl_Position = _12.projection * float4(world_pos * _12.dpi_scale, 0.0, 1.0);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
BIN
draw/shaders/generated/base_2d.vert.spv
Normal file
BIN
draw/shaders/generated/base_2d.vert.spv
Normal file
Binary file not shown.
210
draw/shaders/source/base_2d.frag
Normal file
210
draw/shaders/source/base_2d.frag
Normal file
@@ -0,0 +1,210 @@
|
||||
#version 450 core
|
||||
|
||||
// --- Inputs from vertex shader ---
|
||||
layout(location = 0) in vec4 f_color;
|
||||
layout(location = 1) in vec2 f_local_or_uv;
|
||||
layout(location = 2) in vec4 f_params;
|
||||
layout(location = 3) in vec4 f_params2;
|
||||
layout(location = 4) flat in uint f_kind_flags;
|
||||
layout(location = 5) flat in float f_rotation;
|
||||
|
||||
// --- Output ---
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
// --- Texture sampler (for tessellated/text path) ---
|
||||
layout(set = 2, binding = 0) uniform sampler2D tex;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SDF helper functions (Inigo Quilez)
|
||||
// All operate in physical pixel space — no dpi_scale needed here.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const float PI = 3.14159265358979;
|
||||
|
||||
float sdCircle(vec2 p, float r) {
|
||||
return length(p) - r;
|
||||
}
|
||||
|
||||
float sdRoundedBox(vec2 p, vec2 b, vec4 r) {
|
||||
r.xy = (p.x > 0.0) ? r.xy : r.zw;
|
||||
r.x = (p.y > 0.0) ? r.x : r.y;
|
||||
vec2 q = abs(p) - b + r.x;
|
||||
return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - r.x;
|
||||
}
|
||||
|
||||
float sdSegment(vec2 p, vec2 a, vec2 b) {
|
||||
vec2 pa = p - a, ba = b - a;
|
||||
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
||||
return length(pa - ba * h);
|
||||
}
|
||||
|
||||
float sdEllipse(vec2 p, vec2 ab) {
|
||||
p = abs(p);
|
||||
if (p.x > p.y) {
|
||||
p = p.yx;
|
||||
ab = ab.yx;
|
||||
}
|
||||
float l = ab.y * ab.y - ab.x * ab.x;
|
||||
float m = ab.x * p.x / l;
|
||||
float m2 = m * m;
|
||||
float n = ab.y * p.y / l;
|
||||
float n2 = n * n;
|
||||
float c = (m2 + n2 - 1.0) / 3.0;
|
||||
float c3 = c * c * c;
|
||||
float q = c3 + m2 * n2 * 2.0;
|
||||
float d = c3 + m2 * n2;
|
||||
float g = m + m * n2;
|
||||
float co;
|
||||
if (d < 0.0) {
|
||||
float h = acos(q / c3) / 3.0;
|
||||
float s = cos(h);
|
||||
float t = sin(h) * sqrt(3.0);
|
||||
float rx = sqrt(-c * (s + t + 2.0) + m2);
|
||||
float ry = sqrt(-c * (s - t + 2.0) + m2);
|
||||
co = (ry + sign(l) * rx + abs(g) / (rx * ry) - m) / 2.0;
|
||||
} else {
|
||||
float h = 2.0 * m * n * sqrt(d);
|
||||
float s = sign(q + h) * pow(abs(q + h), 1.0 / 3.0);
|
||||
float u = sign(q - h) * pow(abs(q - h), 1.0 / 3.0);
|
||||
float rx = -s - u - c * 4.0 + 2.0 * m2;
|
||||
float ry = (s - u) * sqrt(3.0);
|
||||
float rm = sqrt(rx * rx + ry * ry);
|
||||
co = (ry / sqrt(rm - rx) + 2.0 * g / rm - m) / 2.0;
|
||||
}
|
||||
vec2 r = ab * vec2(co, sqrt(1.0 - co * co));
|
||||
return length(r - p) * sign(p.y - r.y);
|
||||
}
|
||||
|
||||
float sdf_alpha(float d, float soft) {
|
||||
return 1.0 - smoothstep(-soft, soft, d);
|
||||
}
|
||||
|
||||
float sdf_stroke(float d, float stroke_width) {
|
||||
return abs(d) - stroke_width * 0.5;
|
||||
}
|
||||
|
||||
// Rotate a 2D point by the negative of the given angle (inverse rotation).
|
||||
// Used to rotate the sampling frame opposite to the shape's rotation so that
|
||||
// the SDF evaluates correctly for the rotated shape.
|
||||
vec2 apply_rotation(vec2 p, float angle) {
|
||||
float cr = cos(-angle);
|
||||
float sr = sin(-angle);
|
||||
return mat2(cr, sr, -sr, cr) * p;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void main() {
|
||||
uint kind = f_kind_flags & 0xFFu;
|
||||
uint flags = (f_kind_flags >> 8u) & 0xFFu;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Kind 0: Tessellated path. Texture multiply for text atlas,
|
||||
// white pixel for solid shapes.
|
||||
// -----------------------------------------------------------------------
|
||||
if (kind == 0u) {
|
||||
out_color = f_color * texture(tex, f_local_or_uv);
|
||||
return;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// SDF path. f_local_or_uv = shape-centered position in physical pixels.
|
||||
// All dimensional params are already in physical pixels (CPU pre-scaled).
|
||||
// -----------------------------------------------------------------------
|
||||
float d = 1e30;
|
||||
float soft = 1.0;
|
||||
|
||||
if (kind == 1u) {
|
||||
// RRect: rounded box
|
||||
vec2 b = f_params.xy; // half_size (phys px)
|
||||
vec4 r = vec4(f_params.zw, f_params2.xy); // corner radii: tr, br, tl, bl
|
||||
soft = max(f_params2.z, 1.0);
|
||||
float stroke_px = f_params2.w;
|
||||
|
||||
vec2 p_local = f_local_or_uv;
|
||||
if (f_rotation != 0.0) {
|
||||
p_local = apply_rotation(p_local, f_rotation);
|
||||
}
|
||||
|
||||
d = sdRoundedBox(p_local, b, r);
|
||||
if ((flags & 1u) != 0u) d = sdf_stroke(d, stroke_px);
|
||||
}
|
||||
else if (kind == 2u) {
|
||||
// Circle — rotationally symmetric, no rotation needed
|
||||
float radius = f_params.x;
|
||||
soft = max(f_params.y, 1.0);
|
||||
float stroke_px = f_params.z;
|
||||
|
||||
d = sdCircle(f_local_or_uv, radius);
|
||||
if ((flags & 1u) != 0u) d = sdf_stroke(d, stroke_px);
|
||||
}
|
||||
else if (kind == 3u) {
|
||||
// Ellipse
|
||||
vec2 ab = f_params.xy;
|
||||
soft = max(f_params.z, 1.0);
|
||||
float stroke_px = f_params.w;
|
||||
|
||||
vec2 p_local = f_local_or_uv;
|
||||
if (f_rotation != 0.0) {
|
||||
p_local = apply_rotation(p_local, f_rotation);
|
||||
}
|
||||
|
||||
d = sdEllipse(p_local, ab);
|
||||
if ((flags & 1u) != 0u) d = sdf_stroke(d, stroke_px);
|
||||
}
|
||||
else if (kind == 4u) {
|
||||
// Segment (capsule line) — no rotation (excluded)
|
||||
vec2 a = f_params.xy; // already in local physical pixels
|
||||
vec2 b = f_params.zw;
|
||||
float width = f_params2.x;
|
||||
soft = max(f_params2.y, 1.0);
|
||||
|
||||
d = sdSegment(f_local_or_uv, a, b) - width * 0.5;
|
||||
}
|
||||
else if (kind == 5u) {
|
||||
// Ring / Arc — rotation handled by CPU angle offset, no shader rotation
|
||||
float inner = f_params.x;
|
||||
float outer = f_params.y;
|
||||
float start_rad = f_params.z;
|
||||
float end_rad = f_params.w;
|
||||
soft = max(f_params2.x, 1.0);
|
||||
|
||||
float r = length(f_local_or_uv);
|
||||
float d_ring = max(inner - r, r - outer);
|
||||
|
||||
// Angular clip
|
||||
float angle = atan(f_local_or_uv.y, f_local_or_uv.x);
|
||||
if (angle < 0.0) angle += 2.0 * PI;
|
||||
float ang_start = mod(start_rad, 2.0 * PI);
|
||||
float ang_end = mod(end_rad, 2.0 * PI);
|
||||
|
||||
float in_arc = (ang_end > ang_start)
|
||||
? ((angle >= ang_start && angle <= ang_end) ? 1.0 : 0.0) : ((angle >= ang_start || angle <= ang_end) ? 1.0 : 0.0);
|
||||
if (abs(ang_end - ang_start) >= 2.0 * PI - 0.001) in_arc = 1.0;
|
||||
|
||||
d = in_arc > 0.5 ? d_ring : 1e30;
|
||||
}
|
||||
else if (kind == 6u) {
|
||||
// Regular N-gon — has its own rotation in params, no Primitive.rotation used
|
||||
float radius = f_params.x;
|
||||
float rotation = f_params.y;
|
||||
float sides = f_params.z;
|
||||
soft = max(f_params.w, 1.0);
|
||||
float stroke_px = f_params2.x;
|
||||
|
||||
vec2 p = f_local_or_uv;
|
||||
float c = cos(rotation), s = sin(rotation);
|
||||
p = mat2(c, -s, s, c) * p;
|
||||
|
||||
float an = PI / sides;
|
||||
float bn = mod(atan(p.y, p.x), 2.0 * an) - an;
|
||||
d = length(p) * cos(bn) - radius;
|
||||
|
||||
if ((flags & 1u) != 0u) d = sdf_stroke(d, stroke_px);
|
||||
}
|
||||
|
||||
float alpha = sdf_alpha(d, soft);
|
||||
out_color = vec4(f_color.rgb, f_color.a * alpha);
|
||||
}
|
||||
67
draw/shaders/source/base_2d.vert
Normal file
67
draw/shaders/source/base_2d.vert
Normal file
@@ -0,0 +1,67 @@
|
||||
#version 450 core
|
||||
|
||||
// ---------- Vertex attributes (used in both modes) ----------
|
||||
layout(location = 0) in vec2 v_position;
|
||||
layout(location = 1) in vec2 v_uv;
|
||||
layout(location = 2) in vec4 v_color;
|
||||
|
||||
// ---------- Outputs to fragment shader ----------
|
||||
layout(location = 0) out vec4 f_color;
|
||||
layout(location = 1) out vec2 f_local_or_uv;
|
||||
layout(location = 2) out vec4 f_params;
|
||||
layout(location = 3) out vec4 f_params2;
|
||||
layout(location = 4) flat out uint f_kind_flags;
|
||||
layout(location = 5) flat out float f_rotation;
|
||||
|
||||
// ---------- Uniforms (single block — avoids spirv-cross reordering on Metal) ----------
|
||||
layout(set = 1, binding = 0) uniform Uniforms {
|
||||
mat4 projection;
|
||||
float dpi_scale;
|
||||
uint mode; // 0 = tessellated, 1 = SDF
|
||||
};
|
||||
|
||||
// ---------- SDF primitive storage buffer ----------
|
||||
struct Primitive {
|
||||
vec4 bounds; // 0-15: min_x, min_y, max_x, max_y
|
||||
uint color; // 16-19: packed u8x4 (unpack with unpackUnorm4x8)
|
||||
uint kind_flags; // 20-23: kind | (flags << 8)
|
||||
float rotation; // 24-27: shader self-rotation in radians
|
||||
float _pad; // 28-31: alignment padding
|
||||
vec4 params; // 32-47: shape params part 1
|
||||
vec4 params2; // 48-63: shape params part 2
|
||||
};
|
||||
|
||||
layout(std430, set = 0, binding = 0) readonly buffer Primitives {
|
||||
Primitive primitives[];
|
||||
};
|
||||
|
||||
// ---------- Entry point ----------
|
||||
void main() {
|
||||
if (mode == 0u) {
|
||||
// ---- Mode 0: Tessellated (legacy) ----
|
||||
f_color = v_color;
|
||||
f_local_or_uv = v_uv;
|
||||
f_params = vec4(0.0);
|
||||
f_params2 = vec4(0.0);
|
||||
f_kind_flags = 0u;
|
||||
f_rotation = 0.0;
|
||||
|
||||
gl_Position = projection * vec4(v_position * dpi_scale, 0.0, 1.0);
|
||||
} else {
|
||||
// ---- Mode 1: SDF instanced quads ----
|
||||
Primitive p = primitives[gl_InstanceIndex];
|
||||
|
||||
vec2 corner = v_position; // unit quad corners: (0,0)-(1,1)
|
||||
vec2 world_pos = mix(p.bounds.xy, p.bounds.zw, corner);
|
||||
vec2 center = 0.5 * (p.bounds.xy + p.bounds.zw);
|
||||
|
||||
f_color = unpackUnorm4x8(p.color);
|
||||
f_local_or_uv = (world_pos - center) * dpi_scale; // shape-centered physical pixels
|
||||
f_params = p.params;
|
||||
f_params2 = p.params2;
|
||||
f_kind_flags = p.kind_flags;
|
||||
f_rotation = p.rotation;
|
||||
|
||||
gl_Position = projection * vec4(world_pos * dpi_scale, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user