Further clean up to stroke shader

This commit is contained in:
Grant Sanderson 2024-08-06 15:45:21 -05:00
parent ec88673e92
commit edb438e5e2

View file

@ -32,23 +32,21 @@ const float COS_THRESHOLD = 0.99;
// Used to determine how many lines to break the curve into // Used to determine how many lines to break the curve into
const float POLYLINE_FACTOR = 30; const float POLYLINE_FACTOR = 30;
const int MAX_STEPS = 32; const int MAX_STEPS = 32;
const float MITER_LIMIT = 3.0; const float MITER_LIMIT = 5.0;
#INSERT emit_gl_Position.glsl #INSERT emit_gl_Position.glsl
#INSERT finalize_color.glsl #INSERT finalize_color.glsl
vec3 get_joint_unit_normal(vec4 joint_product){ vec3 get_joint_unit_normal(vec4 joint_product){
vec3 result = (joint_product.w < COS_THRESHOLD) ? float tol = 1e-8;
joint_product.xyz : v_joint_product[1].xyz; if (length(joint_product.xyz) > tol){
float norm = length(result); return normalize(joint_product.xyz);
return (norm > 1e-5) ? result / norm : vec3(0.0, 0.0, 1.0); }
} if (length(v_joint_product[1].xyz) > tol){
return normalize(v_joint_product[1].xyz);
}
vec4 normalized_joint_product(vec4 joint_product){ return vec3(0.0, 0.0, 1.0);
float norm = length(joint_product);
return (norm > 1e-10) ? joint_product / norm : vec4(0.0, 0.0, 0.0, 1.0);
} }
@ -72,18 +70,12 @@ vec3 project(vec3 vect, vec3 unit_normal){
return vect - dot(vect, unit_normal) * unit_normal; return vect - dot(vect, unit_normal) * unit_normal;
} }
vec3 inverse_joint_product(vec3 vect, vec4 joint_product){ vec3 inverse_vector_product(vec3 vect, vec3 cross_product, float dot_product){
/* /*
If joint_product represents vec4(cross(v1, v2), dot(v1, v2)), Suppose cross(v1, v2) = cross_product and dot(v1, v2) = dot_product.
then given v1, this function recovers v2 Given v1, this function return v2.
*/ */
float dp = joint_product.w; return (vect * dot_product - cross(vect, cross_product)) / dot(vect, vect);
if (abs(dp) > COS_THRESHOLD) return vect;
vec3 cp = joint_product.xyz;
vec3 perp = cross(cp, vect);
float a = dp / dot(vect, vect);
float b = length(cp) / length(cross(vect, perp));
return a * vect + b * perp;
} }
@ -94,29 +86,26 @@ vec3 step_to_corner(vec3 point, vec3 tangent, vec3 unit_normal, vec4 joint_produ
so as to make a joint. so as to make a joint.
*/ */
vec3 unit_tan = normalize(flat_stroke == 0.0 ? project(tangent, unit_normal) : tangent); vec3 unit_tan = normalize(flat_stroke == 0.0 ? project(tangent, unit_normal) : tangent);
vec3 step = normalize(cross(unit_normal, unit_tan)); vec3 step = normalize(cross(unit_normal, unit_tan));
// Check if we can avoid creating a joint // Check if we can avoid creating a joint
if (inner_joint || int(joint_type) == NO_JOINT) return step; if (inner_joint || int(joint_type) == NO_JOINT) return step;
// Find the appropriate unit joint product // Find the angle between
vec4 unit_jp;
if (flat_stroke == 0){ if (flat_stroke == 0){
// Figure out what joint product would be for everything projected onto // Figure out what joint product would be for everything projected onto
// the plane perpendicular to the normal direction (which here would be to_camera) // the plane perpendicular to the normal direction (which here would be to_camera)
vec3 adj_tan = inverse_joint_product(tangent, joint_product); vec3 adj_tan = inverse_vector_product(tangent, joint_product.xyz, joint_product.w);
adj_tan = project(adj_tan, unit_normal); adj_tan = project(adj_tan, unit_normal);
unit_jp = normalized_joint_product(get_joint_product(unit_tan, adj_tan)); joint_product = get_joint_product(unit_tan, adj_tan);
}else {
unit_jp = normalized_joint_product(joint_product);
} }
float cos_angle = unit_jp.w; float cos_angle = (length(joint_product) > 1e-10) ? normalize(joint_product).w : 1.0;
if(cos_angle > 1 - 1e-5) return step; if(cos_angle > 1 - 1e-3) return step;
float sin_angle = sqrt(1 - cos_angle * cos_angle) * sign(dot(joint_product.xyz, unit_normal));
// Adjust based on the joint // Adjust based on the joint
float sin_angle = length(unit_jp.xyz) * sign(dot(unit_jp.xyz, unit_normal));
float shift = (int(joint_type) == MITER_JOINT) ? float shift = (int(joint_type) == MITER_JOINT) ?
(cos_angle + 1.0) / sin_angle : (cos_angle + 1.0) / sin_angle :
(cos_angle - 1.0) / sin_angle; (cos_angle - 1.0) / sin_angle;
@ -138,20 +127,22 @@ void emit_point_with_width(
vec4 joint_color, vec4 joint_color,
bool inner_joint bool inner_joint
){ ){
// Normalize relevant vectors // Find unit normal
vec3 to_camera = camera_position - point; vec3 to_camera = camera_position - point;
vec3 unit_normal = (flat_stroke == 0.0) ? vec3 unit_normal;
normalize(to_camera) : if (flat_stroke == 0.0){
get_joint_unit_normal(joint_product); unit_normal = normalize(to_camera);
// Choose the "outward" normal direction }else{
unit_normal *= sign(dot(unit_normal, to_camera)); unit_normal = get_joint_unit_normal(joint_product);
unit_normal *= sign(dot(unit_normal, to_camera)); // Choose the "outward" normal direction
}
// Figure out the step from the point to the corners of the // Figure out the step from the point to the corners of the
// triangle strip around the polyline // 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, inner_joint);
// TODO, this gives a somewhat nice effect that's like a ribbon mostly with its // TODO, this gives a potentially nice effect that's like a ribbon mostly with its
// broad side to the camera. Currently unused by VMobject // broad side to the camera. Currently hard to access via VMobject
if(flat_stroke == 2.0){ if(flat_stroke == 2.0){
// Rotate the step towards the unit normal by an amount depending // Rotate the step towards the unit normal by an amount depending
// on the camera position // on the camera position