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,
depth: float = 6.0,
num_axis_pieces: int = 20,
gloss: float = 0.5,
**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
if TYPE_CHECKING:
from typing import Callable, Iterable, Sequence, Union, Tuple
from typing import Callable, Iterable, Union, Tuple
import numpy.typing as npt
from manimlib.typing import ManimColor, Vect3, Vect4, Vect3Array
@ -76,12 +76,7 @@ class Mobject(object):
self,
color: ManimColor = WHITE,
opacity: float = 1.0,
# Larger reflectiveness makes things brighter when facing the light
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,
shading: Tuple[float, float, float] = (0.0, 0.0, 0.0),
# For shaders
texture_paths: dict[str, str] | None = None,
# If true, the mobject will not get rotated according to camera position
@ -90,9 +85,7 @@ class Mobject(object):
):
self.color = color
self.opacity = opacity
self.reflectiveness = reflectiveness
self.shadow = shadow
self.gloss = gloss
self.shading = shading
self.texture_paths = texture_paths
self.is_fixed_in_frame = is_fixed_in_frame
self.depth_test = depth_test
@ -138,9 +131,7 @@ class Mobject(object):
def init_uniforms(self):
self.uniforms: dict[str, float | np.ndarray] = {
"is_fixed_in_frame": float(self.is_fixed_in_frame),
"gloss": self.gloss,
"shadow": self.shadow,
"reflectiveness": self.reflectiveness,
"shading": np.array(self.shading, dtype=float),
}
def init_colors(self):
@ -1333,28 +1324,46 @@ class Mobject(object):
def fade(self, darkness: float = 0.5, recurse: bool = True):
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:
return self.uniforms["reflectiveness"]
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
return self.get_shading()[0]
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):
for mob in self.get_family(recurse):
mob.uniforms["gloss"] = gloss
self.set_shading(gloss=gloss, recurse=recurse)
return self
def set_shadow(self, shadow: float, recurse: bool = True):
self.set_shading(shadow=shadow, recurse=recurse)
return self
# Background rectangle

View file

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

View file

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

View file

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

View file

@ -36,9 +36,7 @@ class Surface(Mobject):
def __init__(
self,
color: ManimColor = GREY,
reflectiveness: float = 0.3,
gloss: float = 0.1,
shadow: float = 0.4,
shading: Tuple[float, float, float] = (0.3, 0.2, 0.4),
depth_test: bool = True,
u_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__(
**kwargs,
color=color,
reflectiveness=reflectiveness,
gloss=gloss,
shadow=shadow,
shading=shading,
depth_test=depth_test,
)
self.compute_triangle_indices()
@ -300,7 +296,7 @@ class TexturedSurface(Surface):
self.resolution: Tuple[int, int] = uv_surface.resolution
super().__init__(
texture_paths=texture_paths,
gloss=uv_surface.gloss,
shading=tuple(uv_surface.shading),
**kwargs
)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,32 +7,18 @@ in vec3 du_point;
in vec3 dv_point;
in vec4 rgba;
out vec3 xyz_coords;
out vec3 v_normal;
out vec4 v_color;
#INSERT get_gl_Position.glsl
#INSERT get_rotated_surface_unit_normal_vector.glsl
#INSERT get_unit_normal.glsl
#INSERT finalize_color.glsl
void main(){
xyz_coords = position_point_into_frame(point);
v_normal = get_rotated_surface_unit_normal_vector(point, du_point, dv_point);
v_color = rgba;
gl_Position = get_gl_Position(xyz_coords);
gl_Position = get_gl_Position(position_point_into_frame(point));
vec3 normal = get_unit_normal(point, du_point, dv_point);
v_color = finalize_color(rgba, point, normal);
if(clip_plane.xyz != vec3(0.0, 0.0, 0.0)){
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 float num_textures;
in vec3 xyz_coords;
in vec3 v_point;
in vec3 v_normal;
in vec2 v_im_coords;
in float v_opacity;
@ -20,7 +20,7 @@ void main() {
if(num_textures == 2.0){
vec4 dark_color = texture(DarkTexture, v_im_coords);
float dp = dot(
normalize(light_source_position - xyz_coords),
normalize(light_source_position - v_point),
normalize(v_normal)
);
float alpha = smoothstep(-dark_shift, dark_shift, dp);
@ -29,13 +29,8 @@ void main() {
frag_color = finalize_color(
color,
xyz_coords,
normalize(v_normal),
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
v_point,
normalize(v_normal)
);
frag_color.a = v_opacity;
}

View file

@ -6,18 +6,18 @@ in vec3 dv_point;
in vec2 im_coords;
in float opacity;
out vec3 xyz_coords;
out vec3 v_point;
out vec3 v_normal;
out vec2 v_im_coords;
out float v_opacity;
#INSERT get_gl_Position.glsl
#INSERT get_rotated_surface_unit_normal_vector.glsl
#INSERT get_unit_normal.glsl
void main(){
xyz_coords = position_point_into_frame(point);
v_normal = get_rotated_surface_unit_normal_vector(point, du_point, dv_point);
v_point = point;
v_normal = get_unit_normal(point, du_point, dv_point);
v_im_coords = im_coords;
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;
// This include a delaration of
// uniform vec3 shading
// uniform vec3 camera_position
// uniform vec3 light_position
#INSERT finalize_color.glsl
void main() {
@ -21,17 +25,12 @@ void main() {
discard;
}
frag_color = color;
if(gloss > 0 || shadow > 0){
if(shading != vec3(0.0)){
vec3 normal = vec3(diff / radius, sqrt(1 - (dist * dist) / (radius * radius)));
frag_color = finalize_color(
frag_color,
vec3(point.xy, 0.0),
normal,
light_source_position,
camera_position,
reflectiveness,
gloss,
shadow
normal
);
}
if(glow_factor > 0){