Patch for glitches associated with non-flat stroke when tangency direction lines up with camera view

This commit is contained in:
Grant Sanderson 2024-08-08 11:36:28 -05:00
parent 1ff758dea8
commit c345d76de0

View file

@ -85,33 +85,47 @@ vec3 inverse_vector_product(vec3 vect, vec3 cross_product, float dot_product){
}
vec3 step_to_corner(vec3 point, vec3 tangent, vec3 unit_normal, vec4 joint_product, bool inner_joint){
vec3 step_to_corner(vec3 point, vec3 tangent, vec3 unit_normal, vec4 joint_product, bool inside_curve){
/*
Step the the left of a curve.
First a perpendicular direction is calculated, then it is adjusted
so as to make a joint.
*/
vec3 unit_tan = normalize(flat_stroke == 0.0 ? project(tangent, unit_normal) : tangent);
vec4 unit_jp = unit_joint_product(joint_product);
float cos_angle = unit_jp.w;
// Step to stroke width bound should be perpendicular
// both to the tangent and the normal direction
vec3 step = normalize(cross(unit_normal, unit_tan));
// Conditions where no joint needs to be created
if (inner_joint || int(joint_type) == NO_JOINT || cos_angle > COS_THRESHOLD) return step;
// For non-flat stroke, there can be glitches when the tangent direction
// lines up very closely with the direction to the camera, treated here
// as the unit normal. To avoid those, this smoothly transitions to a step
// direction perpendicular to the true curve normal.
float alignment = abs(dot(normalize(tangent), unit_normal));
float alignment_threshold = 0.97; // This could maybe be chosen in a more principled way based on stroke width
if (alignment > alignment_threshold) {
vec3 perp = normalize(cross(get_joint_unit_normal(joint_product), tangent));
step = mix(step, project(step, perp), smoothstep(alignment_threshold, 1.0, alignment));
}
if (inside_curve || int(joint_type) == NO_JOINT) return step;
vec4 unit_jp = unit_joint_product(joint_product);
float cos_angle = unit_jp.w;
if (cos_angle > COS_THRESHOLD) return step;
// Below here, figure out the adjustment to bevel or miter a joint
if (flat_stroke == 0){
// Figure out what joint product would be for everything projected onto
// the plane perpendicular to the normal direction (which here would be to_camera)
step = normalize(cross(unit_normal, unit_tan)); // Back to original step
vec3 adj_tan = inverse_vector_product(tangent, unit_jp.xyz, unit_jp.w);
adj_tan = project(adj_tan, unit_normal);
vec4 flat_jp = get_joint_product(unit_tan, adj_tan);
cos_angle = unit_joint_product(flat_jp).w;
}
// Adjust based on the joint.
// If joint type is auto, it will bevel for cos(angle) > -0.7,
// and smoothly transition to miter for those with sharper angles
float miter_factor;
@ -132,7 +146,7 @@ void emit_point_with_width(
vec4 joint_product,
float width,
vec4 joint_color,
bool inner_joint
bool inside_curve
){
// Find unit normal
vec3 to_camera = camera_position - point;
@ -146,7 +160,7 @@ void emit_point_with_width(
// Figure out the step from the point to the corners of the
// triangle strip around the polyline
vec3 step = step_to_corner(point, tangent, unit_normal, joint_product, inner_joint);
vec3 step = step_to_corner(point, tangent, unit_normal, joint_product, inside_curve);
// TODO, this gives a potentially nice effect that's like a ribbon mostly with its
// broad side to the camera. Currently hard to access via VMobject