mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 12:32:36 +08:00
Fixed the random-dimples-on-zeros bug while fixing up the fill shaders
This commit is contained in:
@ -64,7 +64,6 @@ class VMobject(Mobject):
|
|||||||
('point', np.float32, (3,)),
|
('point', np.float32, (3,)),
|
||||||
('color', np.float32, (4,)),
|
('color', np.float32, (4,)),
|
||||||
('fill_all', np.float32, (1,)),
|
('fill_all', np.float32, (1,)),
|
||||||
('orientation', np.float32, (1,)),
|
|
||||||
],
|
],
|
||||||
"stroke_dtype": [
|
"stroke_dtype": [
|
||||||
("point", np.float32, (3,)),
|
("point", np.float32, (3,)),
|
||||||
@ -984,16 +983,13 @@ class VMobject(Mobject):
|
|||||||
|
|
||||||
# Triangulate
|
# Triangulate
|
||||||
inner_verts = points[inner_vert_indices]
|
inner_verts = points[inner_vert_indices]
|
||||||
inner_tri_indices = inner_vert_indices[
|
inner_tri_indices = inner_vert_indices[earclip_triangulation(inner_verts, rings)]
|
||||||
earclip_triangulation(inner_verts, rings)
|
|
||||||
]
|
|
||||||
|
|
||||||
tri_indices = np.hstack([indices, inner_tri_indices])
|
tri_indices = np.hstack([indices, inner_tri_indices])
|
||||||
return tri_indices
|
return tri_indices
|
||||||
|
|
||||||
def get_fill_shader_data(self):
|
def get_fill_shader_data(self):
|
||||||
points = self.points
|
points = self.points
|
||||||
|
|
||||||
orientation = self.get_orientation()
|
orientation = self.get_orientation()
|
||||||
tri_indices = self.get_triangulation(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
|
# are on the boundary, and the rest are in the interior
|
||||||
data["fill_all"][:len(points)] = 0
|
data["fill_all"][:len(points)] = 0
|
||||||
data["fill_all"][len(points):] = 1
|
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
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
#version 330
|
#version 330
|
||||||
|
|
||||||
in vec4 color;
|
in vec4 color;
|
||||||
in float fill_type;
|
in float fill_all; // Either 0 or 1e
|
||||||
in float uv_anti_alias_width;
|
in float uv_anti_alias_width;
|
||||||
|
|
||||||
in vec2 uv_coords;
|
in vec2 uv_coords;
|
||||||
in vec2 wz_coords;
|
|
||||||
in vec2 uv_b2;
|
in vec2 uv_b2;
|
||||||
in float bezier_degree;
|
in float bezier_degree;
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ const float FILL_OUTSIDE = 1;
|
|||||||
const float FILL_ALL = 2;
|
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){
|
float modify_distance_for_endpoints(vec2 p, float dist, float t){
|
||||||
return dist;
|
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
|
// replaces this line with the contents of quadratic_bezier_sdf.glsl
|
||||||
#INSERT quadratic_bezier_distance.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(){
|
float sdf(){
|
||||||
if(is_inside_curve()) return -1.0;
|
// For really flat curves, just take the distance to the curve
|
||||||
return min_dist_to_curve(uv_coords, uv_b2, bezier_degree, false);
|
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() {
|
void main() {
|
||||||
if (color.a == 0) discard;
|
if (color.a == 0) discard;
|
||||||
frag_color = color;
|
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 *= smoothstep(1, 0, sdf() / uv_anti_alias_width);
|
||||||
// frag_color.a += 0.2;
|
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,13 @@ uniform vec3 frame_center;
|
|||||||
in vec3 bp[3];
|
in vec3 bp[3];
|
||||||
in vec4 v_color[3];
|
in vec4 v_color[3];
|
||||||
in float v_fill_all[3];
|
in float v_fill_all[3];
|
||||||
in float v_orientation[3];
|
|
||||||
|
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
out float fill_type;
|
out float fill_all;
|
||||||
out float uv_anti_alias_width;
|
out float uv_anti_alias_width;
|
||||||
|
|
||||||
// uv space is where b0 = (0, 0), b1 = (1, 0), and transform is orthogonal
|
// uv space is where b0 = (0, 0), b1 = (1, 0), and transform is orthogonal
|
||||||
out vec2 uv_coords;
|
out vec2 uv_coords;
|
||||||
out vec2 uv_b2;
|
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;
|
out float bezier_degree;
|
||||||
|
|
||||||
@ -38,30 +34,6 @@ const float SQRT5 = 2.236068;
|
|||||||
#INSERT quadratic_bezier_geometry_functions.glsl
|
#INSERT quadratic_bezier_geometry_functions.glsl
|
||||||
#INSERT scale_and_shift_point_for_frame.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(){
|
void emit_simple_triangle(){
|
||||||
for(int i = 0; i < 3; i++){
|
for(int i = 0; i < 3; i++){
|
||||||
color = v_color[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
|
// Tangent vectors
|
||||||
vec2 t01 = normalize(bp1 - bp0);
|
vec2 t01 = normalize(bp1 - bp0);
|
||||||
vec2 t12 = normalize(bp2 - bp1);
|
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
|
// Normal vectors
|
||||||
// Rotate tangent vector 90-degrees clockwise
|
// Rotate tangent vector 90-degrees clockwise
|
||||||
// if the curve is positively oriented, otherwise
|
vec2 n01 = vec2(t01.y, -t01.x);
|
||||||
// rotate it 90-degrees counterclockwise
|
vec2 n12 = vec2(t12.y, -t12.x);
|
||||||
vec2 n01 = orientation * vec2(t01.y, -t01.x);
|
|
||||||
vec2 n12 = orientation * vec2(t12.y, -t12.x);
|
float c_orient = sign(cross(t01, t12));
|
||||||
|
bool fill_in = (c_orient > 0);
|
||||||
|
|
||||||
float aaw = anti_alias_width;
|
float aaw = anti_alias_width;
|
||||||
vec2 nudge1 = fill_in ? 0.5 * aaw * (n01 + n12) : vec2(0);
|
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);
|
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_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_b2 = (xy_to_uv * vec3(bp2, 1)).xy;
|
||||||
uv_anti_alias_width = anti_alias_width / length(bp1 - bp0);
|
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++){
|
for(int i = 0; i < 5; i++){
|
||||||
vec2 corner = corners[coords_index_map[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;
|
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
|
// I haven't a clue why an index map doesn't work just
|
||||||
// as well here, but for some reason it doesn't.
|
// as well here, but for some reason it doesn't.
|
||||||
if(i < 2){
|
if(i < 2) color = v_color[0];
|
||||||
color = v_color[0];
|
else if(i == 2) color = v_color[1];
|
||||||
z = bp[0].z;
|
else color = v_color[2];
|
||||||
}
|
|
||||||
else if(i == 2){
|
|
||||||
color = v_color[1];
|
|
||||||
z = bp[1].z;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
color = v_color[2];
|
|
||||||
z = bp[2].z;
|
|
||||||
}
|
|
||||||
gl_Position = vec4(
|
gl_Position = vec4(
|
||||||
scale_and_shift_point_for_frame(vec3(corner, z)),
|
scale_and_shift_point_for_frame(vec3(corner, z)),
|
||||||
1.0
|
1.0
|
||||||
@ -147,32 +97,30 @@ void emit_pentagon(vec2 bp0, vec2 bp1, vec2 bp2, float orientation){
|
|||||||
|
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
float fill_all = v_fill_all[0];
|
fill_all = v_fill_all[0];
|
||||||
|
|
||||||
if(fill_all == 1){
|
if(fill_all == 1){
|
||||||
fill_type = FILL_ALL;
|
|
||||||
emit_simple_triangle();
|
emit_simple_triangle();
|
||||||
}else{
|
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);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,23 +2,16 @@
|
|||||||
|
|
||||||
in vec3 point;
|
in vec3 point;
|
||||||
in vec4 color;
|
in vec4 color;
|
||||||
// fill_all is 0 or 1
|
in float fill_all; // Either 0 or 1
|
||||||
in float fill_all;
|
|
||||||
// orientation is +1 for counterclockwise curves, -1 otherwise
|
|
||||||
in float orientation;
|
|
||||||
|
|
||||||
out vec3 bp; // Bezier control point
|
out vec3 bp; // Bezier control point
|
||||||
out vec4 v_color;
|
out vec4 v_color;
|
||||||
out float v_fill_all;
|
out float v_fill_all;
|
||||||
out float v_orientation;
|
|
||||||
|
|
||||||
|
|
||||||
#INSERT rotate_point_for_frame.glsl
|
#INSERT rotate_point_for_frame.glsl
|
||||||
|
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
bp = rotate_point_for_frame(point);
|
bp = rotate_point_for_frame(point);
|
||||||
v_color = color;
|
v_color = color;
|
||||||
v_fill_all = fill_all;
|
v_fill_all = fill_all;
|
||||||
v_orientation = orientation;
|
|
||||||
}
|
}
|
@ -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,
|
// might change. The idea is to inform the caller of the degree,
|
||||||
// while also passing tangency information in the linear case.
|
// 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]){
|
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 v01 = (b1 - b0);
|
||||||
vec2 v12 = (b2 - b1);
|
vec2 v12 = (b2 - b1);
|
||||||
bool distinct_01 = length(v01) > epsilon; // v01 is considered nonzero
|
// bool aligned = abs(cross(normalize(v01), normalize(v12))) < angle_threshold;
|
||||||
bool distinct_12 = length(v12) > epsilon; // v12 is considered nonzero
|
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);
|
int n_uniques = int(distinct_01) + int(distinct_12);
|
||||||
if(n_uniques == 2){
|
|
||||||
bool linear = dot(normalize(v01), normalize(v12)) > 1 - epsilon;
|
bool quadratic = (n_uniques == 2) && !aligned;
|
||||||
if(linear){
|
bool linear = (n_uniques == 1) || ((n_uniques == 2) && aligned);
|
||||||
new_points[0] = b0;
|
bool constant = (n_uniques == 0);
|
||||||
new_points[1] = b2;
|
if(quadratic){
|
||||||
return 1;
|
new_points[0] = b0;
|
||||||
}else{
|
new_points[1] = b1;
|
||||||
new_points[0] = b0;
|
new_points[2] = b2;
|
||||||
new_points[1] = b1;
|
return 2;
|
||||||
new_points[2] = b2;
|
}else if(linear){
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}else if(n_uniques == 1){
|
|
||||||
new_points[0] = b0;
|
new_points[0] = b0;
|
||||||
new_points[1] = b2;
|
new_points[1] = b2;
|
||||||
return 1;
|
return 1;
|
||||||
|
Reference in New Issue
Block a user