#version 460 #extension GL_GOOGLE_include_directive : require #extension GL_EXT_nonuniform_qualifier : enable #include "bindless.glsl" #include "shape2d.glsl" layout (location = 0) in vec2 inLocalPos; layout (location = 1) in vec2 inQuadPos; layout (location = 2) flat in uint inShapeIndex; layout (location = 0) out vec4 outFragColor; float sdRoundedRect(vec2 p, vec2 halfSize, float radius) { radius = min(radius, min(halfSize.x, halfSize.y)); vec2 q = abs(p) - halfSize + radius; return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius; } // Approximate, but exact on the boundary which is what the edge and outline need float sdEllipse(vec2 p, vec2 radii) { radii = max(radii, vec2(1e-4)); float k0 = length(p / radii); float k1 = length(p / (radii * radii)); return k0 < 1e-4 ? -min(radii.x, radii.y) : k0 * (k0 - 1.0) / k1; } float sdTriangle(vec2 p, vec2 p0, vec2 p1, vec2 p2) { vec2 e0 = p1 - p0, e1 = p2 - p1, e2 = p0 - p2; vec2 v0 = p - p0, v1 = p - p1, v2 = p - p2; vec2 pq0 = v0 - e0 * clamp(dot(v0, e0) / max(dot(e0, e0), 1e-8), 0.0, 1.0); vec2 pq1 = v1 - e1 * clamp(dot(v1, e1) / max(dot(e1, e1), 1e-8), 0.0, 1.0); vec2 pq2 = v2 - e2 * clamp(dot(v2, e2) / max(dot(e2, e2), 1e-8), 0.0, 1.0); float winding = sign(e0.x * e2.y - e0.y * e2.x); vec2 d = min(min(vec2(dot(pq0, pq0), winding * (v0.x * e0.y - v0.y * e0.x)), vec2(dot(pq1, pq1), winding * (v1.x * e1.y - v1.y * e1.x))), vec2(dot(pq2, pq2), winding * (v2.x * e2.y - v2.y * e2.x))); return -sqrt(d.x) * sign(d.y); } void main() { Shape2D shape = pcs.shapeBuffer.shapes[inShapeIndex]; float dist; if (shape.type == SHAPE_ELLIPSE) { dist = sdEllipse(inLocalPos, shape.halfSize); } else if (shape.type == SHAPE_TRIANGLE) { dist = sdTriangle(inLocalPos, shape.p0, shape.p1, shape.p2); } else { dist = sdRoundedRect(inLocalPos, shape.halfSize, shape.cornerRadius); } // Outlines grow inwards so the outer edge matches the filled shape if (shape.outline > 0.0) { dist = abs(dist + shape.outline * 0.5) - shape.outline * 0.5; } float edgeWidth = max(fwidth(dist), 1e-4); float coverage = clamp(0.5 - dist / edgeWidth, 0.0, 1.0); vec2 uv = mix(shape.uvRect.xy, shape.uvRect.zw, clamp(inQuadPos, 0.0, 1.0)); vec4 texColor = sampleTexture2D(shape.textureId, shape.samplerId, uv); outFragColor = texColor * shape.color; outFragColor.a *= coverage; }