mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Merge 3320-fix-antialiasing-opengl into experimental
This commit is contained in:
parent
76694e528b
commit
5d8ea6a4a3
5 changed files with 87 additions and 154 deletions
|
|
@ -559,6 +559,11 @@ class Text(SVGMobject):
|
|||
# anti-aliasing
|
||||
if height is None and width is None:
|
||||
self.scale(TEXT_MOB_SCALE_FACTOR)
|
||||
|
||||
# Just a temporary hack to get better triangulation
|
||||
# See pr #1552 for details
|
||||
for i in self.submobjects:
|
||||
i.insert_n_curves(len(i.get_all_points()))
|
||||
self.initial_height = self.height
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
|||
|
|
@ -6,21 +6,13 @@ in vec4 color;
|
|||
in float fill_all; // Either 0 or 1
|
||||
in float uv_anti_alias_width;
|
||||
|
||||
in vec3 xyz_coords;
|
||||
in float orientation;
|
||||
in vec2 uv_coords;
|
||||
in vec2 uv_b2;
|
||||
in float bezier_degree;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
// Needed for quadratic_bezier_distance insertion below
|
||||
float modify_distance_for_endpoints(vec2 p, float dist, float t)
|
||||
{
|
||||
return dist;
|
||||
}
|
||||
|
||||
#include "../include/quadratic_bezier_distance.glsl"
|
||||
#define ANTI_ALIASING
|
||||
|
||||
float sdf()
|
||||
{
|
||||
|
|
@ -28,30 +20,15 @@ float sdf()
|
|||
{
|
||||
return abs(uv_coords[1]);
|
||||
}
|
||||
float u2 = uv_b2.x;
|
||||
float v2 = uv_b2.y;
|
||||
// For really flat curves, just take the distance to x-axis
|
||||
if (abs(v2 / u2) < 0.1 * uv_anti_alias_width)
|
||||
{
|
||||
return abs(uv_coords[1]);
|
||||
}
|
||||
// This converts uv_coords to yet another 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.
|
||||
mat2 to_simple_space = mat2(v2, 0, 2 - u2, 4 * v2);
|
||||
vec2 p = to_simple_space * uv_coords;
|
||||
// Sign takes care of whether we should be filling the inside or outside of
|
||||
// curve.
|
||||
float sgn = orientation * sign(v2);
|
||||
float Fp = (p.x * p.x - p.y);
|
||||
if (sgn * Fp <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return min_dist_to_curve(uv_coords, uv_b2, bezier_degree);
|
||||
}
|
||||
vec2 p = uv_coords;
|
||||
float sgn = orientation;
|
||||
float q = (p.x * p.x - p.y);
|
||||
#ifdef ANTI_ALIASING
|
||||
return sgn * q / sqrt(dFdx(q) * dFdx(q) + dFdy(q) * dFdy(q));
|
||||
#endif
|
||||
#ifndef ANTI_ALIASING
|
||||
return -sgn * q;
|
||||
#endif
|
||||
}
|
||||
|
||||
void main()
|
||||
|
|
@ -61,5 +38,10 @@ void main()
|
|||
frag_color = color;
|
||||
if (fill_all == 1.0)
|
||||
return;
|
||||
frag_color.a *= smoothstep(1, 0, sdf() / uv_anti_alias_width);
|
||||
#ifdef ANTI_ALIASING
|
||||
frag_color.a *= 0.5 - sdf(); // Anti-aliasing
|
||||
#endif
|
||||
#ifndef ANTI_ALIASING
|
||||
frag_color.a *= float(sdf() > 0); // No anti-aliasing
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,17 +24,11 @@ in float v_vert_index[3];
|
|||
|
||||
out vec4 color;
|
||||
out float fill_all;
|
||||
out float uv_anti_alias_width;
|
||||
|
||||
out vec3 xyz_coords;
|
||||
out float orientation;
|
||||
// uv space is where b0 = (0, 0), b1 = (1, 0), and transform is orthogonal
|
||||
out vec2 uv_coords;
|
||||
out vec2 uv_b2;
|
||||
out float bezier_degree;
|
||||
|
||||
vec3 unit_normal;
|
||||
|
||||
// Analog of import for manim only
|
||||
// Comment to prevent formatting
|
||||
#include "../include/quadratic_bezier_geometry_functions.glsl"
|
||||
|
|
@ -45,12 +39,13 @@ vec3 unit_normal;
|
|||
//
|
||||
#include "../include/finalize_color.glsl"
|
||||
|
||||
const vec2 uv_coords_arr[3] = vec2[3](vec2(0, 0), vec2(0.5, 0), vec2(1, 1));
|
||||
|
||||
void emit_vertex_wrapper(vec3 point, int index)
|
||||
{
|
||||
color = finalize_color(v_color[index], point, unit_normal, light_source_position, camera_position, reflectiveness,
|
||||
gloss, shadow);
|
||||
xyz_coords = point;
|
||||
gl_Position = get_gl_Position(xyz_coords);
|
||||
color = finalize_color(v_color[index], point, v_global_unit_normal[index], light_source_position, gloss, shadow);
|
||||
gl_Position = get_gl_Position(point);
|
||||
uv_coords = uv_coords_arr[index];
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
|
|
@ -63,52 +58,6 @@ void emit_simple_triangle()
|
|||
EndPrimitive();
|
||||
}
|
||||
|
||||
void emit_pentagon(vec3[3] points, vec3 normal)
|
||||
{
|
||||
vec3 p0 = points[0];
|
||||
vec3 p1 = points[1];
|
||||
vec3 p2 = points[2];
|
||||
// Tangent vectors
|
||||
vec3 t01 = normalize(p1 - p0);
|
||||
vec3 t12 = normalize(p2 - p1);
|
||||
// Vectors perpendicular to the curve in the plane of the curve pointing
|
||||
// outside the curve
|
||||
vec3 p0_perp = cross(t01, normal);
|
||||
vec3 p2_perp = cross(t12, normal);
|
||||
|
||||
bool fill_inside = orientation > 0.0;
|
||||
float aaw = anti_alias_width * frame_shape.y / pixel_shape.y;
|
||||
vec3 corners[5];
|
||||
if (bezier_degree == 1.0)
|
||||
{
|
||||
// For straight lines, buff out in both directions
|
||||
corners = vec3[5](p0 + aaw * p0_perp, p0 - aaw * p0_perp, p1 + 0.5 * aaw * (p0_perp + p2_perp),
|
||||
p2 - aaw * p2_perp, p2 + aaw * p2_perp);
|
||||
}
|
||||
else if (fill_inside)
|
||||
{
|
||||
// If curved, and filling insight, just buff out away interior
|
||||
corners = vec3[5](p0 + aaw * p0_perp, p0, p1 + 0.5 * aaw * (p0_perp + p2_perp), p2, p2 + aaw * p2_perp);
|
||||
}
|
||||
else
|
||||
{
|
||||
corners = vec3[5](p0, p0 - aaw * p0_perp, p1, p2 - aaw * p2_perp, p2);
|
||||
}
|
||||
|
||||
mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, normal);
|
||||
uv_b2 = (xyz_to_uv * vec4(p2, 1)).xy;
|
||||
uv_anti_alias_width = aaw / length(p1 - p0);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
vec3 corner = corners[i];
|
||||
uv_coords = (xyz_to_uv * vec4(corner, 1)).xy;
|
||||
int j = int(sign(i - 1) + 1); // Maps i = [0, 1, 2, 3, 4] onto j = [0, 0, 1, 2, 2]
|
||||
emit_vertex_wrapper(corner, j);
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
// If vert indices are sequential, don't fill all
|
||||
|
|
@ -127,7 +76,7 @@ void main()
|
|||
|
||||
if (bezier_degree >= 1)
|
||||
{
|
||||
emit_pentagon(new_bp, unit_normal);
|
||||
emit_simple_triangle();
|
||||
}
|
||||
// Don't emit any vertices for bezier_degree 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -671,73 +671,70 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list) -> list:
|
|||
list
|
||||
A list of indices giving a triangulation of a polygon.
|
||||
"""
|
||||
# First, connect all the rings so that the polygon
|
||||
# with holes is instead treated as a (very convex)
|
||||
# polygon with one edge. Do this by drawing connections
|
||||
# between rings close to each other
|
||||
rings = [list(range(e0, e1)) for e0, e1 in zip([0, *ring_ends], ring_ends)]
|
||||
attached_rings = rings[:1]
|
||||
detached_rings = rings[1:]
|
||||
loop_connections = {}
|
||||
|
||||
while detached_rings:
|
||||
i_range, j_range = (
|
||||
list(
|
||||
filter(
|
||||
# Ignore indices that are already being
|
||||
# used to draw some connection
|
||||
lambda i: i not in loop_connections,
|
||||
it.chain(*ring_group),
|
||||
),
|
||||
)
|
||||
for ring_group in (attached_rings, detached_rings)
|
||||
rings = [list(range(e0, e1)) for e0, e1 in zip([0, *ring_ends], ring_ends)]
|
||||
|
||||
def is_in(point, ring_id):
|
||||
return (
|
||||
abs(abs(get_winding_number([i - point for i in verts[rings[ring_id]]])) - 1)
|
||||
< 1e-5
|
||||
)
|
||||
|
||||
# Closest point on the attached rings to an estimated midpoint
|
||||
# of the detached rings
|
||||
tmp_j_vert = midpoint(verts[j_range[0]], verts[j_range[len(j_range) // 2]])
|
||||
i = min(i_range, key=lambda i: norm_squared(verts[i] - tmp_j_vert))
|
||||
# Closest point of the detached rings to the aforementioned
|
||||
# point of the attached rings
|
||||
j = min(j_range, key=lambda j: norm_squared(verts[i] - verts[j]))
|
||||
# Recalculate i based on new j
|
||||
i = min(i_range, key=lambda i: norm_squared(verts[i] - verts[j]))
|
||||
def ring_area(ring_id):
|
||||
ring = rings[ring_id]
|
||||
s = 0
|
||||
for i, j in zip(ring[1:], ring):
|
||||
s += cross2d(verts[i], verts[j])
|
||||
return abs(s) / 2
|
||||
|
||||
# Remember to connect the polygon at these points
|
||||
loop_connections[i] = j
|
||||
loop_connections[j] = i
|
||||
# Points at the same position may cause problems
|
||||
for i in rings:
|
||||
verts[i[0]] += (verts[i[1]] - verts[i[0]]) * 1e-6
|
||||
verts[i[-1]] += (verts[i[-2]] - verts[i[-1]]) * 1e-6
|
||||
|
||||
# Move the ring which j belongs to from the
|
||||
# attached list to the detached list
|
||||
new_ring = next(filter(lambda ring: ring[0] <= j < ring[-1], detached_rings))
|
||||
detached_rings.remove(new_ring)
|
||||
attached_rings.append(new_ring)
|
||||
# First, we should know which rings are directly contained in it for each ring
|
||||
|
||||
# Setup linked list
|
||||
after = []
|
||||
end0 = 0
|
||||
for end1 in ring_ends:
|
||||
after.extend(range(end0 + 1, end1))
|
||||
after.append(end0)
|
||||
end0 = end1
|
||||
right = [max(verts[rings[i], 0]) for i in range(len(rings))]
|
||||
left = [min(verts[rings[i], 0]) for i in range(len(rings))]
|
||||
top = [max(verts[rings[i], 1]) for i in range(len(rings))]
|
||||
bottom = [min(verts[rings[i], 1]) for i in range(len(rings))]
|
||||
area = [ring_area(i) for i in range(len(rings))]
|
||||
|
||||
# Find an ordering of indices walking around the polygon
|
||||
indices = []
|
||||
i = 0
|
||||
for _ in range(len(verts) + len(ring_ends) - 1):
|
||||
# starting = False
|
||||
if i in loop_connections:
|
||||
j = loop_connections[i]
|
||||
indices.extend([i, j])
|
||||
i = after[j]
|
||||
else:
|
||||
indices.append(i)
|
||||
i = after[i]
|
||||
if i == 0:
|
||||
break
|
||||
# The larger ring must be outside
|
||||
rings_sorted = list(range(len(rings)))
|
||||
rings_sorted.sort(key=lambda x: area[x], reverse=True)
|
||||
|
||||
meta_indices = earcut(verts[indices, :2], [len(indices)])
|
||||
return [indices[mi] for mi in meta_indices]
|
||||
def is_in_fast(ring_a, ring_b):
|
||||
# Whether a is in b
|
||||
return (
|
||||
left[ring_b] <= left[ring_a] <= right[ring_a] <= right[ring_b]
|
||||
and bottom[ring_b] <= bottom[ring_a] <= top[ring_a] <= top[ring_b]
|
||||
and is_in(verts[rings[ring_a][0]], ring_b)
|
||||
)
|
||||
|
||||
children = [[]] * len(rings)
|
||||
for idx, i in enumerate(rings_sorted):
|
||||
for j in rings_sorted[:idx][::-1]:
|
||||
if is_in_fast(i, j):
|
||||
children[j].append(i)
|
||||
break
|
||||
|
||||
res = []
|
||||
|
||||
# Then, we can use earcut for each part
|
||||
used = [False] * len(rings)
|
||||
for i in rings_sorted:
|
||||
if used[i]:
|
||||
continue
|
||||
v = rings[i]
|
||||
ring_ends = [len(v)]
|
||||
for j in children[i]:
|
||||
used[j] = True
|
||||
v += rings[j]
|
||||
ring_ends.append(len(v))
|
||||
res += [v[i] for i in earcut(verts[v, :2], ring_ends)]
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def cartesian_to_spherical(vec: Sequence[float]) -> np.ndarray:
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue