Skip to content

Commit

Permalink
Add stroke and glow to WGLMakie (#3518)
Browse files Browse the repository at this point in the history
* formatting changes

* match GLMakie scaling

* add stroke and glow

* update NEWS [skip ci]

* try enabling more tests

* fix missing uniforms

* update docs + cleanup comments
  • Loading branch information
ffreyer authored Dec 29, 2023
1 parent 40f502b commit cdade9b
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 20 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## master

- Changes for Bonito rename and WGLMakie docs improvements [#3477](https://github.com/MakieOrg/Makie.jl/pull/3477).
- Add stroke and glow support to scatter and text in WGLMakie [#3518](https://github.com/MakieOrg/Makie.jl/pull/3518)

## 0.20.3

Expand Down
62 changes: 53 additions & 9 deletions WGLMakie/assets/sprites.frag
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ in vec2 frag_uv;

// Half width of antialiasing smoothstep
#define ANTIALIAS_RADIUS 0.8

in float frag_uvscale;
in float frag_distancefield_scale;
in vec4 frag_uv_offset_width;
flat in uint frag_instance_id;

// These versions of aastep assume that `dist` is a signed distance function
// which has been scaled to be in units of pixels.
float aastep(float threshold1, float dist) {
Expand Down Expand Up @@ -58,9 +64,23 @@ void fill(sampler2D image, vec4 fillcolor, vec2 uv, float infill, inout vec4 col
color = mix(color, im_color, infill);
}

in float frag_uvscale;
in float frag_distancefield_scale;
in vec4 frag_uv_offset_width;
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
Expand All @@ -73,7 +93,6 @@ float scaled_distancefield(bool distancefield, vec2 uv){
return 0.0;
}

flat in uint frag_instance_id;
vec4 pack_int(uint id, uint index) {
vec4 unpack;
unpack.x = float((id & uint(0xff00)) >> 8) / 255.0;
Expand All @@ -84,32 +103,57 @@ vec4 pack_int(uint id, uint index) {
}

void main() {

int shape = get_shape_type();
float signed_distance = 0.0;

vec4 uv_off = frag_uv_offset_width;
vec2 tex_uv = mix(uv_off.xy, uv_off.zw, clamp(frag_uv, 0.0, 1.0));

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);
else if(shape == TRIANGLE)
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);
}
return;
}


if (final_color.a <= 0.0){
discard;
}
Expand Down
30 changes: 23 additions & 7 deletions WGLMakie/assets/sprites.vert
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ out float frag_uvscale;
out float frag_distancefield_scale;
out vec4 frag_uv_offset_width;

flat out uint frag_instance_id;

#define ANTIALIAS_RADIUS 0.8


mat4 qmat(vec4 quat){
float num = quat.x * 2.0;
Expand Down Expand Up @@ -54,11 +58,12 @@ float _determinant(mat2 m) {
return m[0][0] * m[1][1] - m[0][1] * m[1][0];
}

flat out uint frag_instance_id;

void main(){
vec2 bbox_signed_radius = 0.5 * get_markersize(); // note; components may be negative.
vec2 sprite_bbox_centre = get_quad_offset() + bbox_signed_radius;
// get_pos() returns the position of the scatter marker
// get_position() returns the (relative) position of the current quad vertex

vec2 bbox_radius = 0.5 * get_markersize();
vec2 sprite_bbox_centre = get_quad_offset() + bbox_radius;

mat4 pview = projection * view;
mat4 trans = get_transform_marker() ? model : mat4(1.0);
Expand Down Expand Up @@ -111,14 +116,25 @@ void main(){
// any calculation based on them will not be a distance function.)
// * For sampled distance fields, we need to consistently choose the *x*
// for the scaling in get_distancefield_scale().
float sprite_from_u_scale = abs(get_markersize().x);
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_signed_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();

Expand Down
7 changes: 7 additions & 0 deletions WGLMakie/src/particles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ function scatter_shader(scene::Scene, attributes, plot)
# id + picking gets filled in JS, needs to be here to emit the correct shader uniforms
uniform_dict[:picking] = false
uniform_dict[:object_id] = UInt32(0)

# Make sure these exist
get!(uniform_dict, :strokewidth, 0f0)
get!(uniform_dict, :strokecolor, RGBAf(0, 0, 0, 0))
get!(uniform_dict, :glowwidth, 0f0)
get!(uniform_dict, :glowcolor, RGBAf(0, 0, 0, 0))

return InstancedProgram(WebGL(), lasset("sprites.vert"), lasset("sprites.frag"),
instance, VertexArray(; per_instance...), uniform_dict)
end
Expand Down
3 changes: 0 additions & 3 deletions WGLMakie/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,8 @@ excludes = Set([
"fast pixel marker",
"Array of Images Scatter",
"Image Scatter different sizes",
"scatter with stroke",
"scatter with glow",
"lines and linestyles",
"Textured meshscatter", # not yet implemented
"BezierPath marker stroke", # not yet implemented
])
Makie.inline!(Makie.automatic)

Expand Down
1 change: 0 additions & 1 deletion docs/explanations/backends/wglmakie.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Moving more of the implementation to JavaScript is currently the goal and will g

#### Missing Backend Features

* glow & stroke for scatter markers aren't implemented yet
* `lines(...)` just creates unconnected linesegments and `linestyle` isn't supported

#### Browser Support
Expand Down

0 comments on commit cdade9b

Please sign in to comment.