Fixed the random-dimples-on-zeros bug while fixing up the fill shaders

This commit is contained in:
Grant Sanderson
2020-05-31 17:02:05 -07:00
parent 270e93f6f0
commit 00dcc14df1
5 changed files with 80 additions and 134 deletions

View File

@ -64,7 +64,6 @@ class VMobject(Mobject):
('point', np.float32, (3,)),
('color', np.float32, (4,)),
('fill_all', np.float32, (1,)),
('orientation', np.float32, (1,)),
],
"stroke_dtype": [
("point", np.float32, (3,)),
@ -984,16 +983,13 @@ class VMobject(Mobject):
# Triangulate
inner_verts = points[inner_vert_indices]
inner_tri_indices = inner_vert_indices[
earclip_triangulation(inner_verts, rings)
]
inner_tri_indices = inner_vert_indices[earclip_triangulation(inner_verts, rings)]
tri_indices = np.hstack([indices, inner_tri_indices])
return tri_indices
def get_fill_shader_data(self):
points = self.points
orientation = self.get_orientation()
tri_indices = self.get_triangulation(orientation)
@ -1007,8 +1003,9 @@ class VMobject(Mobject):
# are on the boundary, and the rest are in the interior
data["fill_all"][:len(points)] = 0
data["fill_all"][len(points):] = 1
data["orientation"] = orientation
# Always send points in a positively oriented way
if orientation < 0:
data["point"][:len(points)] = points[::-1]
return data

View File

@ -1,11 +1,10 @@
#version 330
in vec4 color;
in float fill_type;
in float fill_all; // Either 0 or 1e
in float uv_anti_alias_width;
in vec2 uv_coords;
in vec2 wz_coords;
in vec2 uv_b2;
in float bezier_degree;
@ -16,7 +15,7 @@ const float FILL_OUTSIDE = 1;
const float FILL_ALL = 2;
// Needed for quadratic_bezier_distance
// Needed for quadratic_bezier_distance insertion below
float modify_distance_for_endpoints(vec2 p, float dist, float t){
return dist;
}
@ -26,27 +25,36 @@ float modify_distance_for_endpoints(vec2 p, float dist, float t){
// replaces this line with the contents of quadratic_bezier_sdf.glsl
#INSERT quadratic_bezier_distance.glsl
bool is_inside_curve(){
if(bezier_degree < 2) return false;
float value = wz_coords.x * wz_coords.x - wz_coords.y;
if(fill_type == FILL_INSIDE) return value < 0;
if(fill_type == FILL_OUTSIDE) return value > 0;
return false;
}
float sdf(){
if(is_inside_curve()) return -1.0;
return min_dist_to_curve(uv_coords, uv_b2, bezier_degree, false);
// For really flat curves, just take the distance to the curve
if(bezier_degree < 2 || abs(uv_b2.y / uv_b2.x) < uv_anti_alias_width){
return min_dist_to_curve(uv_coords, uv_b2, bezier_degree, false);
}
// This converts uv_coords to a space where the bezier points sit on
// (0, 0), (1/2, 0) and (1, 1), so that the curve can be expressed implicityly
// as y = x^2.
float u2 = uv_b2.x;
float v2 = uv_b2.y;
mat2 to_simple_space = mat2(
v2, 0,
2 - u2, 4 * v2
);
vec2 p = to_simple_space * uv_coords;
float Fp = sign(v2) * (p.x * p.x - p.y);
vec2 grad = vec2(
- 2 * p.x * v2, // del C / del u
4 * v2 - 4 * p.x * (2 - u2) // del C / del v
);
return Fp / length(grad);
}
void main() {
if (color.a == 0) discard;
frag_color = color;
if (fill_type == FILL_ALL) return;
// TODO, Add shading based on normal vector, light position and gloss
if (fill_all == 1.0) return;
frag_color.a *= smoothstep(1, 0, sdf() / uv_anti_alias_width);
// frag_color.a += 0.2;
}

View File

@ -11,17 +11,13 @@ uniform vec3 frame_center;
in vec3 bp[3];
in vec4 v_color[3];
in float v_fill_all[3];
in float v_orientation[3];
out vec4 color;
out float fill_type;
out float fill_all;
out float uv_anti_alias_width;
// uv space is where b0 = (0, 0), b1 = (1, 0), and transform is orthogonal
out vec2 uv_coords;
out vec2 uv_b2;
// wz space is where b0 = (0, 0), b1 = (0.5, 0), b2 = (1, 1)
out vec2 wz_coords;
out float bezier_degree;
@ -38,30 +34,6 @@ const float SQRT5 = 2.236068;
#INSERT quadratic_bezier_geometry_functions.glsl
#INSERT scale_and_shift_point_for_frame.glsl
mat3 get_xy_to_wz(vec2 b0, vec2 b1, vec2 b2){
// If linear or null, this matrix is not needed
if(bezier_degree < 2) return mat3(1.0);
vec2 inv_col1 = 2 * (b1 - b0);
vec2 inv_col2 = b2 - 2 * b1 + b0;
float inv_det = cross(inv_col1, inv_col2);
mat3 transform = mat3(
inv_col2.y, -inv_col1.y, 0,
-inv_col2.x, inv_col1.x, 0,
0, 0, inv_det
) / inv_det;
mat3 shift = mat3(
1, 0, 0,
0, 1, 0,
-b0.x, -b0.y, 1
);
return transform * shift;
}
void emit_simple_triangle(){
for(int i = 0; i < 3; i++){
color = v_color[i];
@ -75,29 +47,17 @@ void emit_simple_triangle(){
}
void emit_pentagon(vec2 bp0, vec2 bp1, vec2 bp2, float orientation){
void emit_pentagon(vec2 bp0, vec2 bp1, vec2 bp2){
// Tangent vectors
vec2 t01 = normalize(bp1 - bp0);
vec2 t12 = normalize(bp2 - bp1);
// Inside and left turn -> rot right -> -1
// Outside and left turn -> rot left -> +1
// Inside and right turn -> rot left -> +1
// Outside and right turn -> rot right -> -1
float c_orient = (cross(t01, t12) > 0) ? 1 : -1;
c_orient *= orientation;
bool fill_in = (c_orient > 0);
fill_type = fill_in ? FILL_INSIDE : FILL_OUTSIDE;
// float orient = in_or_out * c_orient;
// Normal vectors
// Rotate tangent vector 90-degrees clockwise
// if the curve is positively oriented, otherwise
// rotate it 90-degrees counterclockwise
vec2 n01 = orientation * vec2(t01.y, -t01.x);
vec2 n12 = orientation * vec2(t12.y, -t12.x);
vec2 n01 = vec2(t01.y, -t01.x);
vec2 n12 = vec2(t12.y, -t12.x);
float c_orient = sign(cross(t01, t12));
bool fill_in = (c_orient > 0);
float aaw = anti_alias_width;
vec2 nudge1 = fill_in ? 0.5 * aaw * (n01 + n12) : vec2(0);
@ -113,29 +73,19 @@ void emit_pentagon(vec2 bp0, vec2 bp1, vec2 bp2, float orientation){
if(!fill_in) coords_index_map = int[5](1, 0, 2, 4, 3);
mat3 xy_to_uv = get_xy_to_uv(bp0, bp1);
mat3 xy_to_wz = get_xy_to_wz(bp0, bp1, bp2);
uv_b2 = (xy_to_uv * vec3(bp2, 1)).xy;
uv_anti_alias_width = anti_alias_width / length(bp1 - bp0);
int nearest_bp_index_map[5] = int[5](0, 0, 1, 2, 2);
for(int i = 0; i < 5; i++){
vec2 corner = corners[coords_index_map[i]];
float z = bp[nearest_bp_index_map[i]].z;
uv_coords = (xy_to_uv * vec3(corner, 1)).xy;
wz_coords = (xy_to_wz * vec3(corner, 1)).xy;
float z;
// I haven't a clue why an index map doesn't work just
// as well here, but for some reason it doesn't.
if(i < 2){
color = v_color[0];
z = bp[0].z;
}
else if(i == 2){
color = v_color[1];
z = bp[1].z;
}
else{
color = v_color[2];
z = bp[2].z;
}
if(i < 2) color = v_color[0];
else if(i == 2) color = v_color[1];
else color = v_color[2];
gl_Position = vec4(
scale_and_shift_point_for_frame(vec3(corner, z)),
1.0
@ -147,32 +97,30 @@ void emit_pentagon(vec2 bp0, vec2 bp1, vec2 bp2, float orientation){
void main(){
float fill_all = v_fill_all[0];
fill_all = v_fill_all[0];
if(fill_all == 1){
fill_type = FILL_ALL;
emit_simple_triangle();
}else{
vec2 new_bp[3];
int n = get_reduced_control_points(bp[0].xy, bp[1].xy, bp[2].xy, new_bp);
bezier_degree = float(n);
float orientation = v_orientation[0];
vec2 bp0, bp1, bp2;
if(n == 0){
return; // Don't emit any vertices
}
else if(n == 1){
bp0 = new_bp[0];
bp2 = new_bp[1];
bp1 = 0.5 * (bp0 + bp2);
}else{
bp0 = new_bp[0];
bp1 = new_bp[1];
bp2 = new_bp[2];
}
emit_pentagon(bp0, bp1, bp2, orientation);
return;
}
vec2 new_bp[3];
int n = get_reduced_control_points(bp[0].xy, bp[1].xy, bp[2].xy, new_bp);
bezier_degree = float(n);
vec2 bp0, bp1, bp2;
if(n == 0){
return; // Don't emit any vertices
}
else if(n == 1){
bp0 = new_bp[0];
bp2 = new_bp[1];
bp1 = 0.5 * (bp0 + bp2);
}else{
bp0 = new_bp[0];
bp1 = new_bp[1];
bp2 = new_bp[2];
}
emit_pentagon(bp0, bp1, bp2);
}

View File

@ -2,23 +2,16 @@
in vec3 point;
in vec4 color;
// fill_all is 0 or 1
in float fill_all;
// orientation is +1 for counterclockwise curves, -1 otherwise
in float orientation;
in float fill_all; // Either 0 or 1
out vec3 bp; // Bezier control point
out vec4 v_color;
out float v_fill_all;
out float v_orientation;
#INSERT rotate_point_for_frame.glsl
void main(){
bp = rotate_point_for_frame(point);
v_color = color;
v_fill_all = fill_all;
v_orientation = orientation;
}

View File

@ -30,25 +30,25 @@ mat3 get_xy_to_uv(vec2 b0, vec2 b1){
// might change. The idea is to inform the caller of the degree,
// while also passing tangency information in the linear case.
int get_reduced_control_points(vec2 b0, vec2 b1, vec2 b2, out vec2 new_points[3]){
float epsilon = 1e-6;
float length_threshold = 1e-6;
float angle_threshold = 1e-3;
vec2 v01 = (b1 - b0);
vec2 v12 = (b2 - b1);
bool distinct_01 = length(v01) > epsilon; // v01 is considered nonzero
bool distinct_12 = length(v12) > epsilon; // v12 is considered nonzero
// bool aligned = abs(cross(normalize(v01), normalize(v12))) < angle_threshold;
bool aligned = acos(dot(normalize(v01), normalize(v12))) < angle_threshold;
bool distinct_01 = length(v01) > length_threshold; // v01 is considered nonzero
bool distinct_12 = length(v12) > length_threshold; // v12 is considered nonzero
int n_uniques = int(distinct_01) + int(distinct_12);
if(n_uniques == 2){
bool linear = dot(normalize(v01), normalize(v12)) > 1 - epsilon;
if(linear){
new_points[0] = b0;
new_points[1] = b2;
return 1;
}else{
new_points[0] = b0;
new_points[1] = b1;
new_points[2] = b2;
return 2;
}
}else if(n_uniques == 1){
bool quadratic = (n_uniques == 2) && !aligned;
bool linear = (n_uniques == 1) || ((n_uniques == 2) && aligned);
bool constant = (n_uniques == 0);
if(quadratic){
new_points[0] = b0;
new_points[1] = b1;
new_points[2] = b2;
return 2;
}else if(linear){
new_points[0] = b0;
new_points[1] = b2;
return 1;