Skip to content

Commit

Permalink
Rework line shaders for WGLMakie compatability (#3558)
Browse files Browse the repository at this point in the history
* prototyping

* fix joint with non-uniform linewidths

* fix transform

* add patterns

* fix pattern sampling

* fix truncated join cutoff + some cleanup

* improve pattern overwrite threshhold

* reorganize code

* cleanup

* add function for line vec -> line normal

* don't try to cleanup joints if linewidth difference is critical

* rename some variables

* cleanup comments, restructure linewidth sdf generation

* minor cleanup + notes

* fix orientation problems with truncated joints

* minor performance tweaks + cleanup

* handle line joints in fragment shader & ditch variable linewidths

* improve truncation overlap/gap

* improve pattern adjustments for truncated joints

* cleanup

* reorganize code to reduce memory usage

* mostly fix gap in sharp joints

* explain pattern overwrite a bit more

* use new fragment shader for linesegments

* disable debug rendering

* fix incorrect line placement

* adjust line segments to pattern

* use const over define & fix dots

* tweak debug rendering

* make truncation overlap a bit nicer

* minor cleanup

* fix linestyle in linesegments

* skip rendering at 0 linewidth

* add slight bias to avoid missing pixels

* differentiate different segments in debug render

* make color interpolation continuous at joint

* fix dense line color artifacts

* remove unused

* allocate space for joints

* use sdf for AA + debug render

* add miter joints

* handle colors

* some cleanup + linesegments prep

* fix some errors [skip ci]

* add patterns

* clean up linesegments

* add normal rendering mode

* minor cleanup

* experiment with geometry adjustments

* use rounding to solve joint over/underdraw

* WebGL doesn't like scaling

* smooth out inner edge of truncated join

* remove derivative to reduce float accuracy issues

* improve thin lines

* fix lastlen error

* match buffer sizes to number of drawn segments

* avoid dissipation of lines at high point densities

* calculate uv from quad_sdf1

* reduce number of interleaved buffers

* fix disappearing grid lines

* fix missing preprocessors

* fix pattern artifacts in dense line sections

* cleanup some variables

* allow patterns to adjust to linewidth

* fix AA at line end

* fix nan handling

* discard fully transparent pixels

* fix patterns with nan, reset pattern on nan

* fix size issues

* extrude lines slightly to cleanly close them

* fix pixel skipping?

* cleanup patterns

* remove linewidth from pattern based line adjustments

* consider AA in shape factor

* reset shape_factor if pattern changes segment shape

* minor cleanup

* apply changes to WGLMakie + cleanup

* cleanup pattern overwrite

* use the same AA_RADIUS in fragment shader

* fix linelen transform

* disable debug

* fix line start/end AA

* fix px_per_unit?

* fix px_per_unit?

* reduce line start/end extrusion

* simplify extrusion and shape_factor

* summarize breaking changes

* move miter joint test

* update docstring

* minor cleanup

* enable more tests

* move gappy & friends to Makie

* note change to linestyle scaling

* cleanup

* fix line start/end AA

* remove global var

* disable GeoMakie tests for now

* improve transparency for WGLMakie

* fix problems with 180° change in line direction

* fix directionality

* interpolate colormap in fragment shader

* remove util.vert from line/segments

---------

Co-authored-by: SimonDanisch <[email protected]>
  • Loading branch information
ffreyer and SimonDanisch authored Feb 27, 2024
1 parent d0b423c commit 3f885c3
Show file tree
Hide file tree
Showing 23 changed files with 2,414 additions and 1,416 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## [Unreleased]

- **Breaking** Reworked line shaders in GLMakie and WGLMakie [#3558](https://github.com/MakieOrg/Makie.jl/pull/3558)
- GLMakie: Removed support for per point linewidths
- GLMakie: Adjusted dots (e.g. with `linestyle = :dot`) to bend across a joint
- GLMakie: Adjusted linestyles to scale with linewidth dynamically so that dots remain dots with changing linewidth
- GLMakie: Cleaned up anti-aliasing for truncated joints
- WGLMakie: Added support for linestyles
- WGLMakie: Added line joints
- WGLMakie: Added native anti-aliasing which generally improves quality but introduces outline artifacts in some cases (same as GLMakie)
- Both: Adjusted handling of thin lines which may result in different color intensities

## [0.20.8] - 2024-02-22

- Fixed excessive use of space with HTML image outputs [#3642](https://github.com/MakieOrg/Makie.jl/pull/3642).
Expand Down
3 changes: 2 additions & 1 deletion CairoMakie/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ excludes = Set([
"scatter with glow",
"scatter with stroke",
"heatmaps & surface",
"Textured meshscatter" # not yet implemented
"Textured meshscatter", # not yet implemented
"Miter Joints for line rendering", # CairoMakie does not show overlap here and extrudes lines a little more
])

functions = [:volume, :volume!, :uv_mesh]
Expand Down
168 changes: 91 additions & 77 deletions GLMakie/assets/shader/line_segment.geom
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{{GLSL_VERSION}}
{{GLSL_EXTENSIONS}}

{{define_fast_path}}

struct Nothing{ //Nothing type, to encode if some variable doesn't contain any data
bool _; //empty structs are not allowed
};
Expand All @@ -14,37 +12,36 @@ uniform vec2 resolution;
uniform float pattern_length;
{{pattern_type}} pattern;

in vec4 g_color[];
in {{stripped_color_type}} g_color[];
in uvec2 g_id[];
in float g_thickness[];

out float f_thickness;
out vec4 f_color;
out vec2 f_uv;
flat out uvec2 f_id;
flat out vec2 f_uv_minmax;

#define AA_THICKNESS 4.0

// Get pattern values
float fetch(Nothing _, float u){return 10000000.0;}
float fetch(sampler1D pattern, float u){return texture(pattern, u).x;}
out float f_quad_sdf0;
out vec3 f_quad_sdf1;
out float f_quad_sdf2;
out vec2 f_truncation;
out float f_linestart;
out float f_linelength;

vec3 screen_space(vec4 vertex)
{
return vec3(vertex.xy * resolution, vertex.z) / vertex.w;
flat out float f_linewidth;
flat out vec4 f_pattern_overwrite;
flat out uvec2 f_id;
flat out vec2 f_extrusion;
flat out vec2 f_discard_limit;
flat out {{stripped_color_type}} f_color1;
flat out {{stripped_color_type}} f_color2;
flat out float f_alpha_weight;
flat out float f_cumulative_length;

const float AA_RADIUS = 0.8;
const float AA_THICKNESS = 2.0 * AA_RADIUS;

vec3 screen_space(vec4 vertex) {
return vec3((0.5 * vertex.xy / vertex.w + 0.5) * resolution, vertex.z / vertex.w);
}

void emit_vertex(vec3 position, vec2 uv, int index)
{
vec4 inpos = gl_in[index].gl_Position;
f_uv = uv;
f_color = g_color[index];
gl_Position = vec4((position.xy / resolution), position.z, 1.0);
f_id = g_id[index];
f_thickness = g_thickness[index];
EmitVertex();
}
vec2 normal_vector(in vec2 v) { return vec2(-v.y, v.x); }
vec2 normal_vector(in vec3 v) { return vec2(-v.y, v.x); }

out vec3 o_view_pos;
out vec3 o_view_normal;
Expand All @@ -54,54 +51,71 @@ void main(void)
o_view_pos = vec3(0);
o_view_normal = vec3(0);

// get the four vertices passed to the shader:
vec3 p0 = screen_space(gl_in[0].gl_Position); // start of previous segment
vec3 p1 = screen_space(gl_in[1].gl_Position); // end of previous segment, start of current segment

float thickness_aa0 = g_thickness[0] + AA_THICKNESS;
float thickness_aa1 = g_thickness[1] + AA_THICKNESS;
// determine the direction of each of the 3 segments (previous, current, next)
vec3 vun0 = p1 - p0;
vec3 v0 = vun0 / length(vun0.xy);
// determine the normal of each of the 3 segments (previous, current, next)
vec3 n0 = vec3(-v0.y, v0.x, 0);
float l = length(p1 - p0);
float px2u = 0.5 / pattern_length;
float u = l * px2u;

vec3 AA_offset = AA_THICKNESS * v0;
float AA = AA_THICKNESS * px2u;

/* 0 v0 l
| --> |
-thickness_aa0 - .----------------------------------. - -thickness_aa1
-g_thickness[0] - | .------------------------------. | - -g_thickness[1]
| | | |
n0 ↑ | | | |
| | | |
+g_thickness[0] - | '------------------------------' | - +g_thickness[1]
+thickness_aa0 - '----------------------------------' - +thickness_aa1
| |
-AA_THICKNESS l + AA_THICKNESS
*/

#ifdef FAST_PATH
// For solid lines the uv cordinates are used as a signed distance field.
// We keep the values positive to draw a solid line and add limits to
// f_uv_minmax to add anti-aliasing at the start and end of the line
f_uv_minmax = vec2(u, 2*u);
emit_vertex(p0 + thickness_aa0 * n0 - AA_offset, vec2( u - AA, -thickness_aa0), 0);
emit_vertex(p0 - thickness_aa0 * n0 - AA_offset, vec2( u - AA, thickness_aa0), 0);
emit_vertex(p1 + thickness_aa1 * n0 + AA_offset, vec2(2*u + AA, -thickness_aa1), 1);
emit_vertex(p1 - thickness_aa1 * n0 + AA_offset, vec2(2*u + AA, thickness_aa1), 1);
#else
// For patterned lines AA is mostly done by the pattern sampling. We
// still set f_uv_minmax here to ensure that cut off patterns als have
// anti-aliasing at the start/end of this segment
f_uv_minmax = vec2(0, u);
emit_vertex(p0 + thickness_aa0 * n0 - AA_offset, vec2( - AA, -thickness_aa0), 0);
emit_vertex(p0 - thickness_aa0 * n0 - AA_offset, vec2( - AA, thickness_aa0), 0);
emit_vertex(p1 + thickness_aa1 * n0 + AA_offset, vec2(u + AA, -thickness_aa1), 1);
emit_vertex(p1 - thickness_aa1 * n0 + AA_offset, vec2(u + AA, thickness_aa1), 1);
#endif
// we generate very thin lines for linewidth 0, so we manually skip them:
if (g_thickness[0] == 0.0 && g_thickness[1] == 0.0) {
return;
}

// get start and end point of line segment
vec3 p1 = screen_space(gl_in[0].gl_Position);
vec3 p2 = screen_space(gl_in[1].gl_Position);

// get vector in line direction and vector in linewidth direction
vec3 v1 = (p2 - p1);
float segment_length = length(p2.xy - p1.xy);
v1 /= segment_length;
vec2 n1 = normal_vector(v1);

// Set invalid / ignored outputs
f_quad_sdf0 = 10.0; // no joint to previous segment
f_quad_sdf2 = 10.0; // not joint to next segment
f_truncation = vec2(-10.0); // no truncated joint
f_pattern_overwrite = vec4(-1e12, 1.0, 1e12, 1.0); // no joints to overwrite
f_extrusion = vec2(0.5); // no joints needing extrusion
f_discard_limit = vec2(10.0); // no joints needing discards

// constants
f_color1 = g_color[0];
f_color2 = g_color[1];
f_alpha_weight = min(1.0, g_thickness[0] / AA_RADIUS);
f_linestart = 0; // no corners so no joint extrusion to consider
f_linelength = segment_length; // and also no changes in line length
f_cumulative_length = 0.0; // resets for each new segment

// Generate vertices

for (int x = 0; x < 2; x++) {
// Get offset in line direction
float v_offset = (2 * x - 1) * AA_THICKNESS;
// pass on linewidth and id (picking) for the current line vertex
float halfwidth = 0.5 * max(AA_RADIUS, g_thickness[x]);
// TODO: if we just make this a varying output we probably get var linewidths here
f_linewidth = halfwidth;
f_id = g_id[x];

for (int y = 0; y < 2; y++) {
// Get offset in y direction & compute vertex position
float n_offset = (2 * y - 1) * (halfwidth + AA_THICKNESS);
vec3 position = vec3[2](p1, p2)[x] + v_offset * v1 + n_offset * vec3(n1, 0);
gl_Position = vec4(2.0 * position.xy / resolution - 1.0, position.z, 1.0);

// Generate SDF's

// distance from quad vertex to line control points
vec2 VP1 = position.xy - p1.xy;
vec2 VP2 = position.xy - p2.xy;

// sdf of this segment
f_quad_sdf1.x = dot(VP1, -v1.xy);
f_quad_sdf1.y = dot(VP2, v1.xy);
f_quad_sdf1.z = n_offset;

// finalize vertex
EmitVertex();
}
}

EndPrimitive();

return;
}
23 changes: 3 additions & 20 deletions GLMakie/assets/shader/line_segment.vert
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,24 @@ struct Nothing{ //Nothing type, to encode if some variable doesn't contain any d
in float lastlen;
{{vertex_type}} vertex;
{{thickness_type}} thickness;

{{color_type}} color;
{{color_map_type}} color_map;
{{color_norm_type}} color_norm;

uniform mat4 projectionview, model;
uniform uint objectid;
uniform float depth_shift;
uniform float px_per_unit;

out uvec2 g_id;
out vec4 g_color;
out {{stripped_color_type}} g_color;
out float g_thickness;

vec4 getindex(sampler2D tex, int index);
vec4 getindex(sampler1D tex, int index);
vec4 color_lookup(float intensity, sampler1D color_ramp, vec2 norm);

vec4 to_vec4(vec3 v){return vec4(v, 1);}
vec4 to_vec4(vec2 v){return vec4(v, 0, 1);}

vec4 to_color(vec4 v, Nothing color_map, Nothing color_norm, int index){return v;}
vec4 to_color(vec3 v, Nothing color_map, Nothing color_norm, int index){return vec4(v, 1);}
vec4 to_color(sampler1D tex, Nothing color_map, Nothing color_norm, int index){return getindex(tex, index);}
vec4 to_color(sampler2D tex, Nothing color_map, Nothing color_norm, int index){return getindex(tex, index);}
vec4 to_color(float color, sampler1D color_map, vec2 color_norm, int index){
return color_lookup(color, color_map, color_norm);
}


void main()
{
int index = gl_VertexID;
g_id = uvec2(objectid, index+1);
g_color = to_color(color, color_map, color_norm, index);
g_id = uvec2(objectid, gl_VertexID + 1);
g_color = color;
g_thickness = px_per_unit * thickness;
gl_Position = projectionview * model * to_vec4(vertex);
gl_Position.z += gl_Position.w * depth_shift;
Expand Down
Loading

0 comments on commit 3f885c3

Please sign in to comment.