diff --git a/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl b/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl index 898ef399..530b7e69 100644 --- a/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl +++ b/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl @@ -59,8 +59,9 @@ mat3 map_point_pairs(vec2 src0, vec2 src1, vec2 dest0, vec2 dest1){ } -mat3 get_xy_to_uv(vec2 controls[3], float bezier_degree){ +mat3 get_xy_to_uv(vec2 controls[3], float bezier_degree, out float new_bezier_degree){ vec2[2] dest; + new_bezier_degree = bezier_degree; if (bezier_degree == 1.0){ dest[0] = vec2(0, 0); dest[1] = vec2(1, 0); @@ -68,8 +69,15 @@ mat3 get_xy_to_uv(vec2 controls[3], float bezier_degree){ vec2 xs = xs_on_clean_parabola(controls); float x0 = xs.x; float x2 = xs.y; - dest[0] = vec2(x0, x0 * x0); - dest[1] = vec2(x2, x2 * x2); + float thresh = 2.0; + if((x0 > thresh && x2 > thresh) || (x0 < -thresh && x2 < -thresh)){ + dest[0] = vec2(0, 0); + dest[1] = vec2(1, 0); + new_bezier_degree = 1.0; + }else{ + dest[0] = vec2(x0, x0 * x0); + dest[1] = vec2(x2, x2 * x2); + } } return map_point_pairs( controls[0], controls[2], dest[0], dest[1] diff --git a/manimlib/shaders/quadratic_bezier_stroke/frag.glsl b/manimlib/shaders/quadratic_bezier_stroke/frag.glsl index 7022dc38..16436d67 100644 --- a/manimlib/shaders/quadratic_bezier_stroke/frag.glsl +++ b/manimlib/shaders/quadratic_bezier_stroke/frag.glsl @@ -12,7 +12,7 @@ in float bezier_degree; out vec4 frag_color; -const float QUICK_DIST_WIDTH = 0.1; +const float QUICK_DIST_WIDTH = 0.2; float cube_root(float x){ @@ -23,17 +23,18 @@ float cube_root(float x){ // Distance from (x0, y0) to the curve y = x^2 float dist_to_curve(float x0, float y0){ if(bezier_degree == 1.0){ + // In this case, the curve will actually have + // been set to equal the x axis return y0; } - if(false && uv_stroke_width < QUICK_DIST_WIDTH){ + if(uv_stroke_width < QUICK_DIST_WIDTH){ // This is a quick approximation for computing // the distance to the curve. // Evaluate F(x, y) = y - x^2 // divide by its gradient's magnitude return (y0 - x0 * x0) / sqrt(1 + 4 * x0 * x0); } - // Otherwise, explicit solve for the minmal distance using the cubic formula - // + // Otherwise, solve for the minimal distance. // The distance squared between (x0, y0) and a point (x, x^2) looks like // // (x0 - x)^2 + (y0 - x^2)^2 = x^4 + (1 - 2y0)x^2 - 2x0 * x + (x0^2 + y0^2) @@ -42,12 +43,7 @@ float dist_to_curve(float x0, float y0){ // // x^3 + (0.5 - y0) * x - 0.5 * x0 = 0 // - // float p = 0.5 - y0; - // float mhq = 0.25 * x0; // negative half of q - // float sqrt_disc = sqrt(mhq * mhq + p * p * p / 27.0); - // float x = cube_root(mhq + sqrt_disc) + cube_root(mhq - sqrt_disc); - // return distance(uv_coords, vec2(x, x * x)); - + // Use two rounds of Newton's method float x = x0; float p = (0.5 - y0); float q = -0.5 * x0; @@ -69,6 +65,5 @@ void main() { float signed_dist = abs(dist_to_curve(x0, y0)) - 0.5 * uv_stroke_width; frag_color = color; - // if(uv_stroke_width > QUICK_DIST_WIDTH) frag_color = vec4(1, 0, 0, 1); frag_color.a *= smoothstep(0.5, -0.5, signed_dist / uv_anti_alias_width); } \ No newline at end of file diff --git a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl index f4ff7f12..40df5ccd 100644 --- a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl +++ b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl @@ -131,7 +131,6 @@ int get_corners( void main() { if (distance(verts[0], verts[1]) == 0 && distance(verts[1], verts[2]) == 0) return; - bezier_degree = (abs(v_joint_angle[1]) < ANGLE_THRESHOLD) ? 1.0 : 2.0; vec3 unit_normal = camera_rotation * vec3(0.0, 0.0, 1.0); // TODO, track true unit normal globally // Control points are projected to the xy plane before drawing, which in turn @@ -150,10 +149,6 @@ void main() { scaled_strokes[i] = v_stroke_width[i] * sf; } - // 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 information float angle_from_prev = v_joint_angle[0]; @@ -167,6 +162,24 @@ void main() { angle_to_next = 0.0; } + // We want to change the coordinates to a space where the curve + // coincides with y = x^2, between some values x0 and x2. Or, in + // the case of a linear curve (bezier degree 1), just put it on + // the segment from (0, 0) to (1, 0) + bezier_degree = (abs(v_joint_angle[1]) < ANGLE_THRESHOLD) ? 1.0 : 2.0; + + float new_bezier_degree; + mat3 xy_to_uv = get_xy_to_uv(flat_controls, bezier_degree, new_bezier_degree); + bezier_degree = new_bezier_degree; + + float scale_factor = length(xy_to_uv[0].xy); + uv_anti_alias_width = scale_factor * anti_alias_width * (frame_shape.y / pixel_shape.y); + + // 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]); + } + // Corners of a bounding region around curve vec2 corners[5]; int n_corners = get_corners( @@ -178,11 +191,6 @@ void main() { int index_map[5] = int[5](0, 0, 1, 2, 2); if(n_corners == 4) index_map[2] = 2; - // Find uv conversion - mat3 xy_to_uv = get_xy_to_uv(flat_controls, bezier_degree); - float scale_factor = length(xy_to_uv[0].xy); - uv_anti_alias_width = scale_factor * anti_alias_width * (frame_shape.y / pixel_shape.y); - // Emit each corner for(int i = 0; i < n_corners; i++){ uv_coords = (xy_to_uv * vec3(corners[i], 1.0)).xy;