From 8400c90b5cf8e3df8d86ce81c27b3fa6b299c3e2 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Thu, 28 Dec 2023 16:58:29 +0100 Subject: [PATCH] add stroke and glow --- WGLMakie/assets/sprites.frag | 45 +++++++++++++++++++++++++++++++++--- WGLMakie/assets/sprites.vert | 15 ++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/WGLMakie/assets/sprites.frag b/WGLMakie/assets/sprites.frag index dd084d8e639..97e04d8fff8 100644 --- a/WGLMakie/assets/sprites.frag +++ b/WGLMakie/assets/sprites.frag @@ -64,6 +64,23 @@ void fill(sampler2D image, vec4 fillcolor, vec2 uv, float infill, inout vec4 col color = mix(color, im_color, infill); } +void stroke(vec4 strokecolor, float signed_distance, float width, inout vec4 color){ + if (width != 0.0){ + float t = aastep(min(width, 0.0), max(width, 0.0), signed_distance); + vec4 bg_color = mix(color, vec4(strokecolor.rgb, 0), float(signed_distance < 0.5 * width)); + color = mix(bg_color, strokecolor, t); + } +} + +void glow(vec4 glowcolor, float signed_distance, float inside, inout vec4 color){ + float glow_width = get_glowwidth(); + float stroke_width = get_strokewidth(); + if (glow_width > 0.0){ + float outside = (abs(signed_distance) - stroke_width) / glow_width; + float alpha = 1.0 - outside; + color = mix(vec4(glowcolor.rgb, glowcolor.a*alpha), color, inside); + } +} float scaled_distancefield(sampler2D distancefield, vec2 uv){ // Glyph distance field units are in pixels. Convert to same distance @@ -94,9 +111,16 @@ void main() { int shape = get_shape_type(); if(shape == CIRCLE) signed_distance = circle(frag_uv); - else if(shape == DISTANCEFIELD) + else if(shape == DISTANCEFIELD) { signed_distance = scaled_distancefield(distancefield, tex_uv); - else if(shape == ROUNDED_RECTANGLE) + if (get_strokewidth() > 0.0 || get_glowwidth() > 0.0) { + // Compensate for the clamping of tex_uv by an approximate + // extension of the signed distance outside the valid texture + // region. + vec2 bufuv = frag_uv - clamp(frag_uv, 0.0, 1.0); + signed_distance -= length(bufuv); + } + } else if(shape == ROUNDED_RECTANGLE) signed_distance = rounded_rectangle(frag_uv, vec2(0.2), vec2(0.8)); else if(shape == RECTANGLE) signed_distance = 1.0; // rectangle(f_uv); @@ -104,9 +128,24 @@ void main() { signed_distance = triangle(frag_uv); signed_distance *= frag_uvscale; - float inside = aastep(0.0, signed_distance); + + + float stroke_width = get_strokewidth(); + float inside_start = max(-stroke_width, 0.0); + float inside = aastep(inside_start, signed_distance); + vec4 final_color = vec4(frag_color.xyz, 0); fill(image, frag_color, frag_uv, inside, final_color); + stroke(get_strokecolor(), signed_distance, -stroke_width, final_color); + glow(get_glowcolor(), signed_distance, aastep(-stroke_width, signed_distance), final_color); + + // debug - show background + // final_color.a = clamp(final_color.a, 0.0, 1.0); + // final_color = vec4( + // vec3(1,0,0) * (1.0 - final_color.a) + final_color.rgb * final_color.a, + // 0.4 + 0.6 * final_color.a + // ); + if (picking) { if (final_color.a > 0.1) { fragment_color = pack_int(object_id, frag_instance_id); diff --git a/WGLMakie/assets/sprites.vert b/WGLMakie/assets/sprites.vert index 2e01298e227..e06dee067e1 100644 --- a/WGLMakie/assets/sprites.vert +++ b/WGLMakie/assets/sprites.vert @@ -119,11 +119,22 @@ void main(){ float sprite_from_u_scale = min(abs(get_markersize().x), abs(get_markersize().y)); frag_uvscale = viewport_from_sprite_scale * sprite_from_u_scale; frag_distancefield_scale = distancefield_scale(); + + // add padding for AA, stroke and glow (non native distancefields don't need + // AA padding but CIRCLE etc do) + vec2 padded_bbox_size = bbox_radius + ( + ANTIALIAS_RADIUS + max(0.0, get_strokewidth()) + max(0.0, get_glowwidth()) + ) / viewport_from_sprite_scale; + vec2 uv_pad_scale = padded_bbox_size / bbox_radius; + frag_color = tovec4(get_color()); - frag_uv = get_uv(); frag_uv_offset_width = get_uv_offset_width(); + // get_uv() returns (0, 0), (1, 0), (0, 1) or (1, 1) + // to accomodate stroke and glowwidth we need to extrude uv's outwards from (0.5, 0.5) + frag_uv = vec2(0.5) + (get_uv() - vec2(0.5)) * uv_pad_scale; + // screen space coordinates of the position - vec4 quad_vertex = (trans * vec4(2.0 * bbox_radius * get_position(), 0.0, 0.0)); + vec4 quad_vertex = (trans * vec4(2.0 * padded_bbox_size * get_position(), 0.0, 0.0)); gl_Position = vclip + quad_vertex; gl_Position.z += gl_Position.w * get_depth_shift();