vulkan: Rewrite rounded rectangle to use SDF distance

We can use this to properly compute distance in scaled situations.
We also now compute coverage with (imperfect) antialiasing.
This commit is contained in:
Benjamin Otte 2023-05-07 01:49:29 +02:00
parent 64bcdb713c
commit 1be21a33d9
5 changed files with 82 additions and 40 deletions

View File

@ -1,5 +1,5 @@
#include "constants.glsl" #include "constants.glsl"
#include "rounded-rect.glsl" #include "rounded-rect.frag.glsl"
#ifndef _CLIP_ #ifndef _CLIP_
#define _CLIP_ #define _CLIP_

View File

@ -0,0 +1,38 @@
#ifndef _ELLIPSE_
#define _ELLIPSE_
struct Ellipse
{
vec2 center;
vec2 radius;
};
float
ellipse_distance (Ellipse r, vec2 p)
{
vec2 e = r.radius;
p = p - r.center;
if (e.x == e.y)
return length (p) - e.x;
/* from https://www.shadertoy.com/view/tt3yz7 */
vec2 pAbs = abs(p);
vec2 ei = 1.0 / e;
vec2 e2 = e*e;
vec2 ve = ei * vec2(e2.x - e2.y, e2.y - e2.x);
vec2 t = vec2(0.70710678118654752, 0.70710678118654752);
for (int i = 0; i < 3; i++) {
vec2 v = ve*t*t*t;
vec2 u = normalize(pAbs - v) * length(t * e - v);
vec2 w = ei * (v + u);
t = normalize(clamp(w, 0.0, 1.0));
}
vec2 nearestAbs = t * e;
float dist = length(pAbs - nearestAbs);
return dot(pAbs, pAbs) < dot(nearestAbs, nearestAbs) ? -dist : dist;
}
#endif

View File

@ -6,6 +6,7 @@ gsk_private_vulkan_include_shaders = [
'rect.frag.glsl', 'rect.frag.glsl',
'rect.vert.glsl', 'rect.vert.glsl',
'rounded-rect.glsl', 'rounded-rect.glsl',
'rounded-rect.frag.glsl',
] ]
gsk_private_vulkan_fragment_shaders = [ gsk_private_vulkan_fragment_shaders = [

View File

@ -0,0 +1,17 @@
#ifndef _ROUNDED_RECT_FRAG_
#define _ROUNDED_RECT_FRAG_
#include "rounded-rect.glsl"
float
rounded_rect_coverage (RoundedRect r, vec2 p)
{
vec2 fw = abs (fwidth (p));
float distance_scale = max (fw.x, fw.y);
float distance = rounded_rect_distance (r, p) / distance_scale;
float coverage = 0.5 - distance;
return clamp (coverage, 0, 1);
}
#endif

View File

@ -1,6 +1,9 @@
#ifndef _ROUNDED_RECT_ #ifndef _ROUNDED_RECT_
#define _ROUNDED_RECT_ #define _ROUNDED_RECT_
#include "ellipse.glsl"
#include "rect.glsl"
struct RoundedRect struct RoundedRect
{ {
vec4 bounds; vec4 bounds;
@ -9,51 +12,34 @@ struct RoundedRect
}; };
float float
ellipsis_dist (vec2 p, vec2 radius) rounded_rect_distance (RoundedRect r, vec2 p)
{ {
vec2 p0 = p / radius; Rect bounds = Rect(vec4(r.bounds));
vec2 p1 = 2.0 * p0 / radius;
return (dot(p0, p0) - 1.0) / length (p1);
}
float float bounds_distance = rect_distance (bounds, p);
ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
{
float d = ellipsis_dist (point - center, radius);
return clamp (0.5 - d, 0.0, 1.0);
}
float
rounded_rect_coverage (RoundedRect r, vec2 p)
{
if (p.x < r.bounds.x || p.y < r.bounds.y ||
p.x >= r.bounds.z || p.y >= r.bounds.w)
return 0.0;
vec2 rad_tl = vec2(r.corner_widths.x, r.corner_heights.x); Ellipse tl = Ellipse (r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x),
vec2 rad_tr = vec2(r.corner_widths.y, r.corner_heights.y); vec2(r.corner_widths.x, r.corner_heights.x));
vec2 rad_br = vec2(r.corner_widths.z, r.corner_heights.z); Ellipse tr = Ellipse (r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y),
vec2 rad_bl = vec2(r.corner_widths.w, r.corner_heights.w); vec2(r.corner_widths.y, r.corner_heights.y));
Ellipse br = Ellipse (r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z),
vec2 ref_tl = r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x); vec2(r.corner_widths.z, r.corner_heights.z));
vec2 ref_tr = r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y); Ellipse bl = Ellipse (r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w),
vec2 ref_br = r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z); vec2(r.corner_widths.w, r.corner_heights.w));
vec2 ref_bl = r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w);
float d_tl = ellipsis_coverage(p, ref_tl, rad_tl); vec4 distances = vec4(ellipse_distance (tl, p),
float d_tr = ellipsis_coverage(p, ref_tr, rad_tr); ellipse_distance (tr, p),
float d_br = ellipsis_coverage(p, ref_br, rad_br); ellipse_distance (br, p),
float d_bl = ellipsis_coverage(p, ref_bl, rad_bl); ellipse_distance (bl, p));
vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl); bvec4 is_out = bvec4(p.x < tl.center.x && p.y < tl.center.y,
p.x > tr.center.x && p.y < tr.center.y,
p.x > br.center.x && p.y > br.center.y,
p.x < bl.center.x && p.y > bl.center.y);
distances = mix (vec4(bounds_distance), distances, is_out);
bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y, vec2 max2 = max (distances.xy, distances.zw);
p.x > ref_tr.x && p.y < ref_tr.y, return max (max2.x, max2.y);
p.x > ref_br.x && p.y > ref_br.y,
p.x < ref_bl.x && p.y > ref_bl.y);
return 1.0 - dot(vec4(is_out), corner_coverages);
} }
RoundedRect RoundedRect