Package reflectiveness, gloss and shadow into a single uniform "shading"

This commit is contained in:
Grant Sanderson 2023-01-16 19:33:57 -08:00
parent 6e6a30c95a
commit c3cd64f68c
18 changed files with 110 additions and 177 deletions

View file

@ -504,7 +504,6 @@ class ThreeDAxes(Axes):
z_normal: Vect3 = DOWN, z_normal: Vect3 = DOWN,
depth: float = 6.0, depth: float = 6.0,
num_axis_pieces: int = 20, num_axis_pieces: int = 20,
gloss: float = 0.5,
**kwargs **kwargs
): ):
Axes.__init__(self, x_range, y_range, **kwargs) Axes.__init__(self, x_range, y_range, **kwargs)

View file

@ -48,7 +48,7 @@ from manimlib.utils.space_ops import rotation_matrix_transpose
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Callable, Iterable, Sequence, Union, Tuple from typing import Callable, Iterable, Union, Tuple
import numpy.typing as npt import numpy.typing as npt
from manimlib.typing import ManimColor, Vect3, Vect4, Vect3Array from manimlib.typing import ManimColor, Vect3, Vect4, Vect3Array
@ -76,12 +76,7 @@ class Mobject(object):
self, self,
color: ManimColor = WHITE, color: ManimColor = WHITE,
opacity: float = 1.0, opacity: float = 1.0,
# Larger reflectiveness makes things brighter when facing the light shading: Tuple[float, float, float] = (0.0, 0.0, 0.0),
reflectiveness: float = 0.0,
# Larger shadow makes faces opposite the light darker
shadow: float = 0.0,
# Makes parts bright where light gets reflected toward the camera
gloss: float = 0.0,
# For shaders # For shaders
texture_paths: dict[str, str] | None = None, texture_paths: dict[str, str] | None = None,
# If true, the mobject will not get rotated according to camera position # If true, the mobject will not get rotated according to camera position
@ -90,9 +85,7 @@ class Mobject(object):
): ):
self.color = color self.color = color
self.opacity = opacity self.opacity = opacity
self.reflectiveness = reflectiveness self.shading = shading
self.shadow = shadow
self.gloss = gloss
self.texture_paths = texture_paths self.texture_paths = texture_paths
self.is_fixed_in_frame = is_fixed_in_frame self.is_fixed_in_frame = is_fixed_in_frame
self.depth_test = depth_test self.depth_test = depth_test
@ -138,9 +131,7 @@ class Mobject(object):
def init_uniforms(self): def init_uniforms(self):
self.uniforms: dict[str, float | np.ndarray] = { self.uniforms: dict[str, float | np.ndarray] = {
"is_fixed_in_frame": float(self.is_fixed_in_frame), "is_fixed_in_frame": float(self.is_fixed_in_frame),
"gloss": self.gloss, "shading": np.array(self.shading, dtype=float),
"shadow": self.shadow,
"reflectiveness": self.reflectiveness,
} }
def init_colors(self): def init_colors(self):
@ -1333,28 +1324,46 @@ class Mobject(object):
def fade(self, darkness: float = 0.5, recurse: bool = True): def fade(self, darkness: float = 0.5, recurse: bool = True):
self.set_opacity(1.0 - darkness, recurse=recurse) self.set_opacity(1.0 - darkness, recurse=recurse)
def get_shading(self) -> np.ndarray:
return self.uniforms["shading"]
def set_shading(
self,
reflectiveness: float | None = None,
gloss: float | None = None,
shadow: float | None = None,
recurse: bool = True
):
"""
Larger reflectiveness makes things brighter when facing the light
Larger shadow makes faces opposite the light darker
Makes parts bright where light gets reflected toward the camera
"""
for mob in self.get_family(recurse):
for i, value in enumerate([reflectiveness, gloss, shadow]):
if value is not None:
mob.uniforms["shading"][i] = value
return self
def get_reflectiveness(self) -> float: def get_reflectiveness(self) -> float:
return self.uniforms["reflectiveness"] return self.get_shading()[0]
def set_reflectiveness(self, reflectiveness: float, recurse: bool = True):
for mob in self.get_family(recurse):
mob.uniforms["reflectiveness"] = reflectiveness
return self
def get_shadow(self) -> float:
return self.uniforms["shadow"]
def set_shadow(self, shadow: float, recurse: bool = True):
for mob in self.get_family(recurse):
mob.uniforms["shadow"] = shadow
return self
def get_gloss(self) -> float: def get_gloss(self) -> float:
return self.uniforms["gloss"] return self.get_shading()[1]
def get_shadow(self) -> float:
return self.get_shading()[2]
def set_reflectiveness(self, reflectiveness: float, recurse: bool = True):
self.set_shading(reflectiveness=reflectiveness, recurse=recurse)
return self
def set_gloss(self, gloss: float, recurse: bool = True): def set_gloss(self, gloss: float, recurse: bool = True):
for mob in self.get_family(recurse): self.set_shading(gloss=gloss, recurse=recurse)
mob.uniforms["gloss"] = gloss return self
def set_shadow(self, shadow: float, recurse: bool = True):
self.set_shading(shadow=shadow, recurse=recurse)
return self return self
# Background rectangle # Background rectangle

View file

@ -552,7 +552,7 @@ class Piano(VGroup):
class Piano3D(VGroup): class Piano3D(VGroup):
def __init__( def __init__(
self, self,
reflectiveness: float = 1.0, shading: Tuple[float, float, float] = (1.0, 0.2, 0.2),
stroke_width: float = 0.25, stroke_width: float = 0.25,
stroke_color: ManimColor = BLACK, stroke_color: ManimColor = BLACK,
key_depth: float = 0.1, key_depth: float = 0.1,
@ -569,6 +569,7 @@ class Piano3D(VGroup):
for key in piano_2d for key in piano_2d
)) ))
self.set_stroke(stroke_color, stroke_width) self.set_stroke(stroke_color, stroke_width)
self.set_shading(*shading)
self.apply_depth_test() self.apply_depth_test()
# Elevate black keys # Elevate black keys

View file

@ -23,7 +23,7 @@ from manimlib.utils.space_ops import z_to_vector
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Tuple, TypeVar from typing import Tuple, TypeVar
from manimlib.typing import ManimColor, Vect3 from manimlib.typing import ManimColor, Vect3, Sequence
T = TypeVar("T", bound=Mobject) T = TypeVar("T", bound=Mobject)
@ -252,7 +252,7 @@ class Cube(SGroup):
self, self,
color: ManimColor = BLUE, color: ManimColor = BLUE,
opacity: float = 1, opacity: float = 1,
gloss: float = 0.5, shading: Tuple[float, float, float] = (0.1, 0.5, 0.1),
square_resolution: Tuple[int, int] = (2, 2), square_resolution: Tuple[int, int] = (2, 2),
side_length: float = 2, side_length: float = 2,
**kwargs, **kwargs,
@ -262,12 +262,9 @@ class Cube(SGroup):
side_length=side_length, side_length=side_length,
color=color, color=color,
opacity=opacity, opacity=opacity,
shading=shading,
) )
super().__init__( super().__init__(*square_to_cube_faces(face), **kwargs)
*square_to_cube_faces(face),
gloss=gloss,
**kwargs
)
class Prism(Cube): class Prism(Cube):
@ -288,16 +285,12 @@ class VGroup3D(VGroup):
self, self,
*vmobjects: VMobject, *vmobjects: VMobject,
depth_test: bool = True, depth_test: bool = True,
gloss: float = 0.2, shading: Tuple[float, float, float] = (0.2, 0.2, 0.2),
shadow: float = 0.2,
reflectiveness: float = 0.2,
joint_type: str = "no_joint", joint_type: str = "no_joint",
**kwargs **kwargs
): ):
super().__init__(*vmobjects, **kwargs) super().__init__(*vmobjects, **kwargs)
self.set_gloss(gloss) self.set_shading(*shading)
self.set_shadow(shadow)
self.set_reflectiveness(reflectiveness)
self.set_joint_type(joint_type) self.set_joint_type(joint_type)
if depth_test: if depth_test:
self.apply_depth_test() self.apply_depth_test()
@ -342,7 +335,7 @@ class Dodecahedron(VGroup3D):
fill_opacity: float = 1, fill_opacity: float = 1,
stroke_color: ManimColor = BLUE_E, stroke_color: ManimColor = BLUE_E,
stroke_width: float = 1, stroke_width: float = 1,
reflectiveness: float = 0.2, shading: Tuple[float, float, float] = (0.2, 0.2, 0.2),
**kwargs, **kwargs,
): ):
style = dict( style = dict(
@ -350,7 +343,7 @@ class Dodecahedron(VGroup3D):
fill_opacity=fill_opacity, fill_opacity=fill_opacity,
stroke_color=stroke_color, stroke_color=stroke_color,
stroke_width=stroke_width, stroke_width=stroke_width,
reflectiveness=reflectiveness, shading=shading,
**kwargs **kwargs
) )

View file

@ -137,9 +137,13 @@ class DotCloud(PMobject):
self.set_radii(scale_factor * self.get_radii()) self.set_radii(scale_factor * self.get_radii())
return self return self
def make_3d(self, reflectiveness: float = 0.5, shadow: float = 0.2): def make_3d(
self.set_reflectiveness(reflectiveness) self,
self.set_shadow(shadow) reflectiveness: float = 0.5,
gloss: float = 0.1,
shadow: float = 0.2
):
self.set_shading(reflectiveness, gloss, shadow)
self.apply_depth_test() self.apply_depth_test()
return self return self

View file

@ -36,9 +36,7 @@ class Surface(Mobject):
def __init__( def __init__(
self, self,
color: ManimColor = GREY, color: ManimColor = GREY,
reflectiveness: float = 0.3, shading: Tuple[float, float, float] = (0.3, 0.2, 0.4),
gloss: float = 0.1,
shadow: float = 0.4,
depth_test: bool = True, depth_test: bool = True,
u_range: Tuple[float, float] = (0.0, 1.0), u_range: Tuple[float, float] = (0.0, 1.0),
v_range: Tuple[float, float] = (0.0, 1.0), v_range: Tuple[float, float] = (0.0, 1.0),
@ -61,9 +59,7 @@ class Surface(Mobject):
super().__init__( super().__init__(
**kwargs, **kwargs,
color=color, color=color,
reflectiveness=reflectiveness, shading=shading,
gloss=gloss,
shadow=shadow,
depth_test=depth_test, depth_test=depth_test,
) )
self.compute_triangle_indices() self.compute_triangle_indices()
@ -300,7 +296,7 @@ class TexturedSurface(Surface):
self.resolution: Tuple[int, int] = uv_surface.resolution self.resolution: Tuple[int, int] = uv_surface.resolution
super().__init__( super().__init__(
texture_paths=texture_paths, texture_paths=texture_paths,
gloss=uv_surface.gloss, shading=tuple(uv_surface.shading),
**kwargs **kwargs
) )

View file

@ -158,7 +158,7 @@ class VMobject(Mobject):
opacity=self.stroke_opacity, opacity=self.stroke_opacity,
background=self.stroke_behind, background=self.stroke_behind,
) )
self.set_gloss(self.gloss) self.set_shading(*self.shading)
self.set_flat_stroke(self.flat_stroke) self.set_flat_stroke(self.flat_stroke)
self.color = self.get_color() self.color = self.get_color()
return self return self
@ -229,9 +229,7 @@ class VMobject(Mobject):
stroke_rgba: Vect4 | None = None, stroke_rgba: Vect4 | None = None,
stroke_width: float | Iterable[float] | None = None, stroke_width: float | Iterable[float] | None = None,
stroke_background: bool = True, stroke_background: bool = True,
reflectiveness: float | None = None, shading: Tuple[float, float, float] | None = None,
gloss: float | None = None,
shadow: float | None = None,
recurse: bool = True recurse: bool = True
): ):
for mob in self.get_family(recurse): for mob in self.get_family(recurse):
@ -260,12 +258,8 @@ class VMobject(Mobject):
background=stroke_background, background=stroke_background,
) )
if reflectiveness is not None: if shading is not None:
mob.set_reflectiveness(reflectiveness, recurse=False) mob.set_shading(*shading, recurse=False)
if gloss is not None:
mob.set_gloss(gloss, recurse=False)
if shadow is not None:
mob.set_shadow(shadow, recurse=False)
return self return self
def get_style(self): def get_style(self):
@ -275,9 +269,7 @@ class VMobject(Mobject):
"stroke_rgba": data['stroke_rgba'].copy(), "stroke_rgba": data['stroke_rgba'].copy(),
"stroke_width": data['stroke_width'].copy(), "stroke_width": data['stroke_width'].copy(),
"stroke_background": self.stroke_behind, "stroke_background": self.stroke_behind,
"reflectiveness": self.get_reflectiveness(), "shading": self.get_shading(),
"gloss": self.get_gloss(),
"shadow": self.get_shadow(),
} }
def match_style(self, vmobject: VMobject, recurse: bool = True): def match_style(self, vmobject: VMobject, recurse: bool = True):
@ -1160,7 +1152,7 @@ class VMobject(Mobject):
self.make_approximately_smooth() self.make_approximately_smooth()
return self return self
def apply_points_function(self, *args, **kwargs,): def apply_points_function(self, *args, **kwargs):
super().apply_points_function(*args, **kwargs) super().apply_points_function(*args, **kwargs)
self.refresh_joint_angles() self.refresh_joint_angles()

View file

@ -1,8 +1,6 @@
uniform vec3 light_source_position; uniform vec3 light_source_position;
uniform vec3 camera_position; uniform vec3 camera_position;
uniform float reflectiveness; uniform vec3 shading;
uniform float gloss;
uniform float shadow;
vec3 float_to_color(float value, float min_val, float max_val, vec3[9] colormap_data){ vec3 float_to_color(float value, float min_val, float max_val, vec3[9] colormap_data){
float alpha = clamp((value - min_val) / (max_val - min_val), 0.0, 1.0); float alpha = clamp((value - min_val) / (max_val - min_val), 0.0, 1.0);
@ -15,23 +13,16 @@ vec3 float_to_color(float value, float min_val, float max_val, vec3[9] colormap_
} }
vec4 add_light( vec4 add_light(vec4 color, vec3 point, vec3 unit_normal){
vec4 color, if(shading == vec3(0.0)) return color;
vec3 point,
vec3 unit_normal, float reflectiveness = shading.x;
vec3 light_coords, float gloss = shading.y;
vec3 cam_coords, float shadow = shading.z;
float reflectiveness,
float gloss,
float shadow
){
if(reflectiveness == 0.0 && gloss == 0.0 && shadow == 0.0) return color;
vec4 result = color; vec4 result = color;
// Assume everything has already been rotated such that camera is in the z-direction vec3 to_camera = normalize(camera_position - point);
// cam_coords = vec3(0, 0, focal_distance); vec3 to_light = normalize(light_position - point);
vec3 to_camera = normalize(cam_coords - point);
vec3 to_light = normalize(light_coords - point);
// Note, this effectively treats surfaces as two-sided // Note, this effectively treats surfaces as two-sided
// if(dot(to_camera, unit_normal) < 0) unit_normal *= -1; // if(dot(to_camera, unit_normal) < 0) unit_normal *= -1;
@ -48,26 +39,18 @@ vec4 add_light(
result.rgb = mix(result.rgb, vec3(1.0), bright_factor); result.rgb = mix(result.rgb, vec3(1.0), bright_factor);
if (light_to_normal < 0){ if (light_to_normal < 0){
// Darken // Darken
result.rgb = mix(result.rgb, vec3(0.0), -light_to_normal * shadow); result.rgb = mix(
result.rgb,
vec3(0.0),
max(-light_to_normal, 0) * shadow
);
} }
return result; return result;
} }
vec4 finalize_color( vec4 finalize_color(vec4 color, vec3 point, vec3 unit_normal){
vec4 color,
vec3 point,
vec3 unit_normal,
vec3 light_coords,
vec3 cam_coords,
float reflectiveness,
float gloss,
float shadow
){
///// INSERT COLOR FUNCTION HERE ///// ///// INSERT COLOR FUNCTION HERE /////
// The line above may be replaced by arbitrary code snippets, as per // The line above may be replaced by arbitrary code snippets, as per
// the method Mobject.set_color_by_code // the method Mobject.set_color_by_code
return add_light( return add_light(color, point, unit_normal);
color, point, unit_normal, light_coords, cam_coords,
reflectiveness, gloss, shadow
);
} }

View file

@ -64,11 +64,6 @@ void main() {
frag_color = finalize_color( frag_color = finalize_color(
vec4(color, opacity), vec4(color, opacity),
xyz_coords, xyz_coords,
vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, 1.0)
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
); );
} }

View file

@ -144,11 +144,6 @@ void main() {
frag_color = finalize_color( frag_color = finalize_color(
color, color,
xyz_coords, xyz_coords,
vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, 1.0)
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
); );
} }

View file

@ -33,14 +33,9 @@ void emit_vertex_wrapper(vec3 point, int index, vec3 unit_normal){
color = finalize_color( color = finalize_color(
v_color[index], v_color[index],
point, point,
unit_normal, unit_normal
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
); );
gl_Position = get_gl_Position(point); gl_Position = get_gl_Position(position_point_into_frame(point));
EmitVertex(); EmitVertex();
} }

View file

@ -10,11 +10,8 @@ out float v_orientation;
out vec4 v_color; out vec4 v_color;
out float v_vert_index; out float v_vert_index;
// Analog of import for manim only
#INSERT get_gl_Position.glsl
void main(){ void main(){
verts = position_point_into_frame(point); verts = point;
v_orientation = orientation; v_orientation = orientation;
v_color = fill_rgba; v_color = fill_rgba;
v_vert_index = vert_index; v_vert_index = vert_index;

View file

@ -177,14 +177,9 @@ void main() {
color = finalize_color( color = finalize_color(
v_color[vert_index], v_color[vert_index],
corners[i], corners[i],
unit_normal, unit_normal
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
); );
gl_Position = get_gl_Position(corners[i]); gl_Position = get_gl_Position(position_point_into_frame(corners[i]));
EmitVertex(); EmitVertex();
} }
EndPrimitive(); EndPrimitive();

View file

@ -1,5 +1,7 @@
#version 330 #version 330
uniform vec2 frame_shape;
in vec3 point; in vec3 point;
in vec4 stroke_rgba; in vec4 stroke_rgba;
in float stroke_width; in float stroke_width;
@ -15,11 +17,8 @@ out float v_vert_index;
const float STROKE_WIDTH_CONVERSION = 0.01; const float STROKE_WIDTH_CONVERSION = 0.01;
#INSERT get_gl_Position.glsl
void main(){ void main(){
verts = position_point_into_frame(point); verts = point;
v_stroke_width = STROKE_WIDTH_CONVERSION * stroke_width * frame_shape[1] / 8.0; v_stroke_width = STROKE_WIDTH_CONVERSION * stroke_width * frame_shape[1] / 8.0;
v_joint_angle = joint_angle; v_joint_angle = joint_angle;
v_color = stroke_rgba; v_color = stroke_rgba;

View file

@ -7,32 +7,18 @@ in vec3 du_point;
in vec3 dv_point; in vec3 dv_point;
in vec4 rgba; in vec4 rgba;
out vec3 xyz_coords;
out vec3 v_normal;
out vec4 v_color; out vec4 v_color;
#INSERT get_gl_Position.glsl #INSERT get_gl_Position.glsl
#INSERT get_rotated_surface_unit_normal_vector.glsl #INSERT get_unit_normal.glsl
#INSERT finalize_color.glsl #INSERT finalize_color.glsl
void main(){ void main(){
xyz_coords = position_point_into_frame(point); gl_Position = get_gl_Position(position_point_into_frame(point));
v_normal = get_rotated_surface_unit_normal_vector(point, du_point, dv_point); vec3 normal = get_unit_normal(point, du_point, dv_point);
v_color = rgba; v_color = finalize_color(rgba, point, normal);
gl_Position = get_gl_Position(xyz_coords);
if(clip_plane.xyz != vec3(0.0, 0.0, 0.0)){ if(clip_plane.xyz != vec3(0.0, 0.0, 0.0)){
gl_ClipDistance[0] = dot(vec4(point, 1.0), clip_plane); gl_ClipDistance[0] = dot(vec4(point, 1.0), clip_plane);
} }
v_color = finalize_color(
rgba,
xyz_coords,
v_normal,
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
);
} }

View file

@ -4,7 +4,7 @@ uniform sampler2D LightTexture;
uniform sampler2D DarkTexture; uniform sampler2D DarkTexture;
uniform float num_textures; uniform float num_textures;
in vec3 xyz_coords; in vec3 v_point;
in vec3 v_normal; in vec3 v_normal;
in vec2 v_im_coords; in vec2 v_im_coords;
in float v_opacity; in float v_opacity;
@ -20,7 +20,7 @@ void main() {
if(num_textures == 2.0){ if(num_textures == 2.0){
vec4 dark_color = texture(DarkTexture, v_im_coords); vec4 dark_color = texture(DarkTexture, v_im_coords);
float dp = dot( float dp = dot(
normalize(light_source_position - xyz_coords), normalize(light_source_position - v_point),
normalize(v_normal) normalize(v_normal)
); );
float alpha = smoothstep(-dark_shift, dark_shift, dp); float alpha = smoothstep(-dark_shift, dark_shift, dp);
@ -29,13 +29,8 @@ void main() {
frag_color = finalize_color( frag_color = finalize_color(
color, color,
xyz_coords, v_point,
normalize(v_normal), normalize(v_normal)
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
); );
frag_color.a = v_opacity; frag_color.a = v_opacity;
} }

View file

@ -6,18 +6,18 @@ in vec3 dv_point;
in vec2 im_coords; in vec2 im_coords;
in float opacity; in float opacity;
out vec3 xyz_coords; out vec3 v_point;
out vec3 v_normal; out vec3 v_normal;
out vec2 v_im_coords; out vec2 v_im_coords;
out float v_opacity; out float v_opacity;
#INSERT get_gl_Position.glsl #INSERT get_gl_Position.glsl
#INSERT get_rotated_surface_unit_normal_vector.glsl #INSERT get_unit_normal.glsl
void main(){ void main(){
xyz_coords = position_point_into_frame(point); v_point = point;
v_normal = get_rotated_surface_unit_normal_vector(point, du_point, dv_point); v_normal = get_unit_normal(point, du_point, dv_point);
v_im_coords = im_coords; v_im_coords = im_coords;
v_opacity = opacity; v_opacity = opacity;
gl_Position = get_gl_Position(xyz_coords); gl_Position = get_gl_Position(position_point_into_frame(point));
} }

View file

@ -11,6 +11,10 @@ in float scaled_aaw;
out vec4 frag_color; out vec4 frag_color;
// This include a delaration of
// uniform vec3 shading
// uniform vec3 camera_position
// uniform vec3 light_position
#INSERT finalize_color.glsl #INSERT finalize_color.glsl
void main() { void main() {
@ -21,17 +25,12 @@ void main() {
discard; discard;
} }
frag_color = color; frag_color = color;
if(gloss > 0 || shadow > 0){ if(shading != vec3(0.0)){
vec3 normal = vec3(diff / radius, sqrt(1 - (dist * dist) / (radius * radius))); vec3 normal = vec3(diff / radius, sqrt(1 - (dist * dist) / (radius * radius)));
frag_color = finalize_color( frag_color = finalize_color(
frag_color, frag_color,
vec3(point.xy, 0.0), vec3(point.xy, 0.0),
normal, normal
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
); );
} }
if(glow_factor > 0){ if(glow_factor > 0){