Various cleanups for stroke shader

This commit is contained in:
Grant Sanderson 2023-01-08 23:33:39 -05:00
parent ae2a253fb1
commit 89d9e260eb
3 changed files with 65 additions and 97 deletions

View file

@ -1,14 +1,11 @@
// Must be inserted in a context with a definition for modify_distance_for_endpoints
// All of this is with respect to a curve that's been rotated/scaled
// so that b0 = (0, 0) and b1 = (1, 0). That is, b2 entirely
// determines the shape of the curve
uniform float joint_type;
vec2 bezier(float t, vec2 b2){
// Quick returns for the 0 and 1 cases
if (t == 0) return vec2(0, 0);
else if (t == 1) return b2;
// Everything else
return vec2(
2 * t * (1 - t) + b2.x * t*t,
b2.y * t * t
@ -64,22 +61,19 @@ int cubic_solve(float a, float b, float c, float d, out float roots[3]){
return n_valid_roots;
}
float dist_to_line(vec2 p, vec2 b2){
float t = clamp(p.x / b2.x, 0, 1);
float dist;
if(t == 0) dist = length(p);
else if(t == 1) dist = distance(p, b2);
else dist = abs(p.y);
return modify_distance_for_endpoints(p, dist, t);
float dist_to_line(vec2 p, vec2 b2){
if(joint_type == 1){
float t = p.x / b2.x;
if (t < 0) return length(p);
if (t > 1) return distance(p, b2);
}
return abs(p.y);
}
float dist_to_point_on_curve(vec2 p, float t, vec2 b2){
t = clamp(t, 0, 1);
return modify_distance_for_endpoints(
p, length(p - bezier(t, b2)), t
);
return length(p - bezier(t, b2));
}

View file

@ -11,81 +11,59 @@ in float uv_anti_alias_width;
in float has_prev;
in float has_next;
in float bevel;
in float angle_from_prev;
in float angle_to_next;
in float bezier_degree;
out vec4 frag_color;
#INSERT quadratic_bezier_distance.glsl
float cross2d(vec2 v, vec2 w){
return v.x * w.y - w.x * v.y;
}
float modify_distance_for_endpoints(vec2 p, float dist, float t){
if (0 <= t && t <= 1) return dist;
float buff = 0.5 * uv_stroke_width - uv_anti_alias_width;
// Check the beginning of the curve
if(t == 0){
// Clip the start
if(has_prev == 0) return max(dist, -p.x + buff);
// Bevel start
if(bevel == 1){
float a = angle_from_prev;
mat2 rot = mat2(
cos(a), sin(a),
-sin(a), cos(a)
);
// Dist for intersection of two lines
float bevel_d = max(abs(p.y), abs((rot * p).y));
// Dist for union of this intersection with the real curve
// intersected with radius 2 away from curve to smooth out
// really sharp corners
return max(min(dist, bevel_d), dist / 2);
}
// Otherwise, start will be rounded off
}else if(t == 1){
if(t < 0 && has_prev == 0) return max(dist, -p.x + buff);
if(t > 1){
// Check the end of the curve
// TODO, too much code repetition
vec2 v21 = (bezier_degree == 2) ? vec2(1, 0) - uv_b2 : vec2(-1, 0);
vec2 v21 = vec2(1, 0) - uv_b2;
float len_v21 = length(v21);
if(len_v21 == 0){
v21 = -uv_b2;
len_v21 = length(v21);
}
if(len_v21 == 0) len_v21 = length(-uv_b2);
float perp_dist = dot(p - uv_b2, v21) / len_v21;
if(has_next == 0) return max(dist, -perp_dist + buff);
// Bevel end
if(bevel == 1){
float a = -angle_to_next;
mat2 rot = mat2(
cos(a), sin(a),
-sin(a), cos(a)
);
vec2 v21_unit = v21 / length(v21);
float bevel_d = max(
abs(cross2d(p - uv_b2, v21_unit)),
abs(cross2d((rot * (p - uv_b2)), v21_unit))
);
return max(min(dist, bevel_d), dist / 2);
}
// Otherwise, end will be rounded off
}
return dist;
}
#INSERT quadratic_bezier_distance.glsl
float dist_to_curve(){
float dist = min_dist_to_curve(uv_coords, uv_b2, bezier_degree);
if(has_prev == 0 && uv_coords.x < 0){
float buff = 0.5 * uv_stroke_width - uv_anti_alias_width;
return max(-uv_coords.x + buff, dist);
}
if(has_next == 0 && uv_coords.x > uv_b2.x){
float buff = 0.5 * uv_stroke_width - uv_anti_alias_width;
vec2 v12 = normalize(uv_b2 - vec2(1, 0));
float perp_dist = dot(uv_coords - uv_b2, v12);
if (perp_dist > 0) return max(perp_dist + buff, dist);
}
return dist;
}
void main() {
if (uv_stroke_width == 0) discard;
float dist_to_curve = min_dist_to_curve(uv_coords, uv_b2, bezier_degree);
// An sdf for the region around the curve we wish to color.
float signed_dist = abs(dist_to_curve) - 0.5 * uv_stroke_width;
float signed_dist = abs(dist_to_curve()) - 0.5 * uv_stroke_width;
frag_color = color;
frag_color.a *= smoothstep(0.5, -0.5, signed_dist / uv_anti_alias_width);

View file

@ -32,17 +32,11 @@ out float uv_anti_alias_width;
out float has_prev;
out float has_next;
out float bevel;
out float angle_from_prev;
out float angle_to_next;
out float bezier_degree;
out vec2 uv_coords;
out vec2 uv_b2;
vec3 unit_normal;
// Codes for joint types
const float AUTO_JOINT = 0;
const float ROUND_JOINT = 1;
@ -93,7 +87,14 @@ void create_joint(float angle, vec2 unit_tan, float buff,
// This function is responsible for finding the corners of
// a bounding region around the bezier curve, which can be
// emitted as a triangle fan
int get_corners(vec2 controls[3], int degree, float stroke_widths[3], out vec2 corners[5]){
int get_corners(
vec2 controls[3],
int degree,
float stroke_widths[3],
float angle_from_prev,
float angle_to_next,
out vec2 corners[5]
){
vec2 p0 = controls[0];
vec2 p1 = controls[1];
vec2 p2 = controls[2];
@ -141,30 +142,9 @@ int get_corners(vec2 controls[3], int degree, float stroke_widths[3], out vec2 c
}
void find_joint_info(){
angle_from_prev = v_joint_angle[0];
angle_to_next = v_joint_angle[2];
has_prev = 1.0;
has_next = 1.0;
if(angle_from_prev == DISJOINT_CONST){
angle_from_prev = 0.0;
has_prev = 0.0;
}
if(angle_to_next == DISJOINT_CONST){
angle_to_next = 0.0;
has_next = 0.0;
}
bool should_bevel = (
(joint_type == AUTO_JOINT && bezier_degree == 1.0) ||
joint_type == BEVEL_JOINT
);
bevel = float(should_bevel);
}
void main() {
bezier_degree = (abs(v_joint_angle[1]) < 1e-3) ? 1.0 : 2.0;
unit_normal = get_unit_normal(vec3[3](verts[0], verts[1], verts[2]));
vec3 unit_normal = get_unit_normal(vec3[3](verts[0], verts[1], verts[2]));
// Adjust stroke width based on distance from the camera
float scaled_strokes[3];
@ -185,17 +165,33 @@ void main() {
float sf = perspective_scale_factor(verts[i].z, focal_distance);
flat_controls[i] = sf * verts[i].xy;
}
// If the curve is flat, put the middle control in the midpoint
if (bezier_degree == 1.0){
flat_controls[1] = 0.5 * (flat_controls[0] + flat_controls[2]);
}
// Set joint angles, etc.
find_joint_info();
// Set joint information
float angle_from_prev = v_joint_angle[0];
float angle_to_next = v_joint_angle[2];
has_prev = 1.0;
has_next = 1.0;
if(angle_from_prev == DISJOINT_CONST){
angle_from_prev = 0.0;
has_prev = 0.0;
}
if(angle_to_next == DISJOINT_CONST){
angle_to_next = 0.0;
has_next = 0.0;
}
// Corners of a bounding region around curve
vec2 corners[5];
int n_corners = get_corners(flat_controls, int(bezier_degree), scaled_strokes, corners);
int n_corners = get_corners(
flat_controls, int(bezier_degree), scaled_strokes,
angle_from_prev, angle_to_next,
corners
);
int index_map[5] = int[5](0, 0, 1, 2, 2);
if(n_corners == 4) index_map[2] = 2;