First pass at a winding-based fill approach

This commit is contained in:
Grant Sanderson
2023-01-24 13:29:34 -08:00
parent d01658bc5b
commit 4774d2bc3b
6 changed files with 128 additions and 137 deletions

View File

@ -2,29 +2,20 @@
in vec4 color;
in float fill_all; // Either 0 or 1
in float uv_anti_alias_width;
in float orientation;
in vec2 uv_coords;
in float is_linear;
out vec4 frag_color;
float sdf(){
float x0 = uv_coords.x;
float y0 = uv_coords.y;
if(bool(is_linear)) return abs(y0);
float Fxy = y0 - x0 * x0;
if(orientation * Fxy >= 0) return 0.0;
return abs(Fxy) / sqrt(1 + 4 * x0 * x0);
}
void main() {
if (color.a == 0) discard;
frag_color = color;
if (bool(fill_all)) return;
frag_color.a *= smoothstep(1, 0, sdf() / uv_anti_alias_width);
if (orientation == 0) return;
float x0 = uv_coords.x;
float y0 = uv_coords.y;
float Fxy = y0 - x0 * x0;
if(orientation * Fxy < 0) discard;
}

View File

@ -1,129 +1,68 @@
#version 330
layout (triangles) in;
layout (triangle_strip, max_vertices = 5) out;
layout (triangle_strip, max_vertices = 7) out;
uniform float anti_alias_width;
uniform float pixel_size;
uniform vec3 corner;
in vec3 verts[3];
in float v_orientation[3];
in vec4 v_color[3];
in vec3 v_base_point[3];
in float v_vert_index[3];
out vec4 color;
out float fill_all;
out float uv_anti_alias_width;
out float orientation;
// uv space is where the curve coincides with y = x^2
out vec2 uv_coords;
out float is_linear;
const float ANGLE_THRESHOLD = 1e-3;
// Analog of import for manim only
#INSERT get_gl_Position.glsl
#INSERT get_xyz_to_uv.glsl
#INSERT get_unit_normal.glsl
#INSERT finalize_color.glsl
void emit_vertex_wrapper(vec3 point, int index, vec3 unit_normal){
color = finalize_color(v_color[index], point, unit_normal);
void emit_vertex_wrapper(vec3 point, vec4 v_color, vec3 unit_normal){
color = finalize_color(v_color, point, unit_normal);
gl_Position = get_gl_Position(point);
EmitVertex();
}
void emit_simple_triangle(vec3 unit_normal){
for(int i = 0; i < 3; i++){
emit_vertex_wrapper(verts[i], i, unit_normal);
}
EndPrimitive();
}
void emit_pentagon(
// Triangle vertices
vec3 p0,
vec3 p1,
vec3 p2,
// Unit tangent vector
vec3 t01,
vec3 t12,
vec3 unit_normal
){
// Vectors perpendicular to the curve in the plane of the curve
// pointing outside the curve
vec3 p0_perp = cross(t01, unit_normal);
vec3 p2_perp = cross(t12, unit_normal);
float angle = acos(clamp(dot(t01, t12), -1, 1));
is_linear = float(angle < ANGLE_THRESHOLD);
if(bool(is_linear)){
// Cross with unit z vector
p0_perp = normalize(vec3(-t01.y, t01.x, 0));
p2_perp = p0_perp;
}
bool fill_inside = orientation > 0.0;
float aaw = anti_alias_width * pixel_size;
vec3 corners[5] = vec3[5](p0, p0, p1, p2, p2);
if(fill_inside || bool(is_linear)){
// Add buffer outside the curve
corners[0] += aaw * p0_perp;
corners[2] += 0.5 * aaw * (p0_perp + p2_perp);
corners[4] += aaw * p2_perp;
} else{
// Add buffer inside the curve
corners[1] -= aaw * p0_perp;
corners[3] -= aaw * p2_perp;
}
// Compute xy_to_uv matrix, and potentially re-evaluate bezier degree
bool too_steep;
mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, p2, 10.0, too_steep);
if(too_steep) is_linear = 1.0;
uv_anti_alias_width = aaw * length(xyz_to_uv[0].xyz);
for(int i = 0; i < 5; i++){
int j = int[5](0, 0, 1, 2, 2)[i];
vec3 corner = corners[i];
uv_coords = (xyz_to_uv * vec4(corner, 1.0)).xy;
emit_vertex_wrapper(corner, j, unit_normal);
}
EndPrimitive();
}
void main(){
// If vert indices are sequential, don't fill all
fill_all = float(
(v_vert_index[1] - v_vert_index[0]) != 1.0 ||
(v_vert_index[2] - v_vert_index[1]) != 1.0
);
// We use the triangle strip primative, but
// actually only need every other strip element
if (int(v_vert_index[0]) % 2 == 1) return;
vec3 p0 = verts[0];
vec3 p1 = verts[1];
vec3 p2 = verts[2];
vec3 t01 = p1 - p0;
vec3 t12 = p2 - p1;
vec3 unit_normal = normalize(cross(t01, t12));
// Curves are marked as eneded when the handle after
// the first anchor is set equal to that anchor
if (verts[0] == verts[1]) return;
if(bool(fill_all)){
emit_simple_triangle(unit_normal);
return;
}
orientation = v_orientation[1];
vec3 unit_normal = get_unit_normal(verts[0], verts[1], verts[2]);
emit_pentagon(
p0, p1, p2,
normalize(t01),
normalize(t12),
unit_normal
);
// Emit main triangle
orientation = 0.0;
uv_coords = vec2(0.0);
emit_vertex_wrapper(verts[2], v_color[2], unit_normal);
emit_vertex_wrapper(v_base_point[0], v_color[1], unit_normal);
emit_vertex_wrapper(verts[0], v_color[0], unit_normal);
// Emit edge triangle
orientation = 1.0;
uv_coords = vec2(0, 0);
// Two dummies
emit_vertex_wrapper(verts[0], v_color[0], unit_normal);
emit_vertex_wrapper(verts[0], v_color[0], unit_normal);
// Inner corner
uv_coords = vec2(0.5, 0);
emit_vertex_wrapper(verts[1], v_color[1], unit_normal);
// Last corner
uv_coords = vec2(1.0, 1.0);
emit_vertex_wrapper(verts[2], v_color[2], unit_normal);
EndPrimitive();
}

View File

@ -2,17 +2,17 @@
in vec3 point;
in vec4 fill_rgba;
in float orientation;
in float vert_index;
in vec3 base_point;
out vec3 verts; // Bezier control point
out float v_orientation;
out vec4 v_joint_product;
out vec4 v_color;
out vec3 v_base_point;
out float v_vert_index;
void main(){
verts = point;
v_orientation = orientation;
v_color = fill_rgba;
v_vert_index = vert_index;
v_base_point = base_point;
v_vert_index = gl_VertexID;
}