Open
Description
Describe the bug
The texture does not render correctly on certain TexturedSurfaces (Sphere, Cone, Disk3D). In specific areas it appears completely black.
Code:
from manimlib import *
class SurfaceExample(Scene):
CONFIG = {
"camera_class": ThreeDCamera,
}
def construct(self):
frame = self.camera.frame
frame.set_euler_angles(
theta=55 * DEGREES,
phi=70 * DEGREES,
)
sphere = Sphere(radius=2.5, resolution=(4, 4))
texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
earth = TexturedSurface(sphere, texture, opacity=0.9)
sphere.shift(LEFT * 3)
earth.shift(RIGHT * 3)
self.play(FadeIn(sphere))
self.play(FadeIn(earth))
self.wait()
Wrong display or Error traceback:
It is mainly noticeable in surfaces with low resolution:
Additional context
The issue occurs at surface points where one of the derivative vectors is degenerate:
point − du_point = 0 or point − dv_point = 0
Potential Fix
I added a conditional check in the texture_surface shader to detect du or dv with zero lengths, in which case the unit_normal must be calculated in a different way.
shaders/textured_surface/vert.glsl:
#version 330
in vec3 point;
in vec3 du_point;
in vec3 dv_point;
in vec2 im_coords;
in float opacity;
out vec3 v_point;
out vec3 v_unit_normal;
out vec2 v_im_coords;
out float v_opacity;
#INSERT emit_gl_Position.glsl
#INSERT get_unit_normal.glsl
void main(){
v_point = point;
vec3 du = du_point - point;
vec3 dv = dv_point - point;
if(length(dv) < 1e6 || length(du) < 1e-6){
v_unit_normal = normalize(point);
} else {
v_unit_normal = normalize(cross(normalize(du), normalize(dv)));
}
v_im_coords = im_coords;
v_opacity = opacity;
emit_gl_Position(point);
}
The analytical normal of a sphere was used in the fallback case.
Results
Conclusion
I am not sure if this was the ideal solution, but it is working perfectly for various resolutions and surfaces now!