From 39230a805cd4b6eb7c339478992a84d1b99bb869 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 8 Jun 2020 15:06:22 -0700 Subject: [PATCH] Better handling of textures, now letting multiple get passed in to a textured surface --- manimlib/camera/camera.py | 9 ++-- manimlib/mobject/mobject.py | 4 +- manimlib/mobject/types/image_mobject.py | 2 +- manimlib/mobject/types/surface.py | 11 +++-- manimlib/mobject/types/vectorized_mobject.py | 2 - manimlib/shaders/textured_surface_frag.glsl | 15 ++++-- manimlib/utils/shaders.py | 49 +++++++++++--------- 7 files changed, 52 insertions(+), 40 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 7212247c..3e3aaf3d 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -367,11 +367,10 @@ class Camera(object): geometry_shader=get_shader_code_from_file(info["geom"]), fragment_shader=get_shader_code_from_file(info["frag"]), ) - if info["texture_path"]: - # TODO, this currently assumes that the uniform Sampler2D - # is named Texture, and that there's only one of them - tid = self.get_texture_id(info["texture_path"]) - shader["Texture"].value = tid + if info["texture_paths"]: + for name, path in info["texture_paths"].items(): + tid = self.get_texture_id(path) + shader[name].value = tid self.set_shader_uniforms(shader) self.id_to_shader[sid] = shader diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index adf2ad97..1209a930 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -50,7 +50,7 @@ class Mobject(Container): "geom_shader_file": "", "frag_shader_file": "", "render_primative": moderngl.TRIANGLE_STRIP, - "texture_path": "", + "texture_paths": None, # Must match in attributes of vert shader "shader_dtype": [ ('point', np.float32, (3,)), @@ -1215,8 +1215,8 @@ class Mobject(Container): vert_file=self.vert_shader_file, geom_file=self.geom_shader_file, frag_file=self.frag_shader_file, - texture_path=self.texture_path, render_primative=self.render_primative, + texture_paths=self.texture_paths, ) def get_shader_data(self): diff --git a/manimlib/mobject/types/image_mobject.py b/manimlib/mobject/types/image_mobject.py index 222ee5c1..dcad3e75 100644 --- a/manimlib/mobject/types/image_mobject.py +++ b/manimlib/mobject/types/image_mobject.py @@ -25,7 +25,7 @@ class ImageMobject(Mobject): def __init__(self, filename, **kwargs): path = get_full_raster_image_path(filename) self.image = Image.open(path) - self.texture_path = path + self.texture_paths = {"Texture": path} Mobject.__init__(self, **kwargs) def init_points(self): diff --git a/manimlib/mobject/types/surface.py b/manimlib/mobject/types/surface.py index 02631004..f90e1ca4 100644 --- a/manimlib/mobject/types/surface.py +++ b/manimlib/mobject/types/surface.py @@ -167,12 +167,15 @@ class TexturedSurface(ParametricSurface): ] } - def __init__(self, uv_surface, filename, **kwargs): + def __init__(self, uv_surface, image_file, dark_image_file=None, **kwargs): if not isinstance(uv_surface, ParametricSurface): raise Exception("uv_surface must be of type ParametricSurface") - path = get_full_raster_image_path(filename) - self.image = Image.open(path) - self.texture_path = path + if dark_image_file is None: + dark_image_file = image_file + self.texture_paths = { + "LightTexture": get_full_raster_image_path(image_file), + "DarkTexture": get_full_raster_image_path(dark_image_file), + } self.uv_surface = uv_surface self.uv_func = uv_surface.uv_func diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index b396269c..bdd4e83f 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -827,14 +827,12 @@ class VMobject(Mobject): vert_file=self.stroke_vert_shader_file, geom_file=self.stroke_geom_shader_file, frag_file=self.stroke_frag_shader_file, - texture_path=self.texture_path, render_primative=self.render_primative, ) fill_info = get_shader_info( vert_file=self.fill_vert_shader_file, geom_file=self.fill_geom_shader_file, frag_file=self.fill_frag_shader_file, - texture_path=self.texture_path, render_primative=self.render_primative, ) diff --git a/manimlib/shaders/textured_surface_frag.glsl b/manimlib/shaders/textured_surface_frag.glsl index 7fdc0545..888a34bd 100644 --- a/manimlib/shaders/textured_surface_frag.glsl +++ b/manimlib/shaders/textured_surface_frag.glsl @@ -1,6 +1,7 @@ #version 330 -uniform sampler2D Texture; +uniform sampler2D LightTexture; +uniform sampler2D DarkTexture; uniform vec3 light_source_position; in vec3 xyz_coords; @@ -15,9 +16,17 @@ out vec4 frag_color; #INSERT add_light.glsl void main() { - vec4 im_color = texture(Texture, v_im_coords); + vec4 light_color = texture(LightTexture, v_im_coords); + vec4 dark_color = texture(DarkTexture, v_im_coords); + float dp = dot( + normalize(light_source_position - xyz_coords), + normalize(v_normal) + ); + float alpha = smoothstep(-0.1, 0.1, dp); + vec4 color = mix(dark_color, light_color, alpha); + frag_color = add_light( - im_color, + color, xyz_coords, normalize(v_normal), light_source_position, diff --git a/manimlib/utils/shaders.py b/manimlib/utils/shaders.py index fb913950..2ebccb79 100644 --- a/manimlib/utils/shaders.py +++ b/manimlib/utils/shaders.py @@ -2,6 +2,7 @@ import os import warnings import re import moderngl +import json from manimlib.constants import SHADER_DIR @@ -13,11 +14,19 @@ from manimlib.constants import SHADER_DIR SHADER_INFO_KEYS = [ + # A structred array caring all of the points/color/lighting/etc. information + # needed for the shader. "data", + # Filename of vetex shader "vert", + # Filename of geometry shader, if there is one "geom", + # Filename of fragment shader "frag", - "texture_path", + # A dictionary mapping names (as they show up in) + # the shader to filepaths for textures. + "texture_paths", + # E.g. moderngl.TRIANGLE_STRIP "render_primative", ] @@ -26,15 +35,18 @@ def get_shader_info(data=None, vert_file=None, geom_file=None, frag_file=None, - texture_path=None, - render_primative=moderngl.TRIANGLE_STRIP): + render_primative=moderngl.TRIANGLE_STRIP, + texture_paths=None, + ): return { key: value for key, value in zip( SHADER_INFO_KEYS, [ - data, vert_file, geom_file, frag_file, - texture_path, str(render_primative) + data, + vert_file, geom_file, frag_file, + texture_paths or {}, + str(render_primative) ] ) } @@ -52,32 +64,23 @@ def is_valid_shader_info(shader_info): def shader_info_to_id(shader_info): # A unique id for a shader based on the # files holding its code and texture - return "|".join([ - shader_info.get(key, "") or "" - for key in SHADER_INFO_KEYS[1:] - ]) + tuples = [ + (key, shader_info[key]) + for key in SHADER_INFO_KEYS[1:] # Skip data + ] + return json.dumps(tuples) def shader_id_to_info(sid): - return { - key: (value or None) - for key, value in zip( - SHADER_INFO_KEYS, - [None, *sid.split("|")] - ) - } + result = dict(json.loads(sid)) + result["data"] = None + return result def same_shader_type(info1, info2): return all([ info1[key] == info2[key] - for key in [ - "vert", - "geom", - "frag", - "texture_path", - "render_primative", - ] + for key in SHADER_INFO_KEYS[1:] # Skip data ])