From 79ec791fc297ff9d8def3f6bf08c2dd23360b0db Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 21 Aug 2024 09:02:22 -0500 Subject: [PATCH] Move get_fill_canvas into VShaderWrapper --- manimlib/shader_wrapper.py | 82 ++++++++++++++++++++++++++++++++++++-- manimlib/utils/shaders.py | 73 --------------------------------- 2 files changed, 78 insertions(+), 77 deletions(-) diff --git a/manimlib/shader_wrapper.py b/manimlib/shader_wrapper.py index daef9210..ed0218ed 100644 --- a/manimlib/shader_wrapper.py +++ b/manimlib/shader_wrapper.py @@ -7,13 +7,15 @@ import re import OpenGL.GL as gl import moderngl import numpy as np +from functools import lru_cache +from manimlib.config import parse_cli +from manimlib.config import get_configuration from manimlib.utils.iterables import resize_array from manimlib.utils.shaders import get_shader_code_from_file from manimlib.utils.shaders import get_shader_program from manimlib.utils.shaders import image_path_to_texture from manimlib.utils.shaders import get_texture_id -from manimlib.utils.shaders import get_fill_canvas from manimlib.utils.shaders import release_texture from manimlib.utils.shaders import set_program_uniform @@ -23,7 +25,6 @@ if TYPE_CHECKING: from typing import List, Optional, Dict from manimlib.typing import UniformDict - # Mobjects that should be rendered with # the same shader will be organized and # clumped together based on keeping track @@ -215,7 +216,6 @@ class VShaderWrapper(ShaderWrapper): stroke_behind: bool = False, ): self.stroke_behind = stroke_behind - self.fill_canvas = get_fill_canvas(ctx) super().__init__( ctx=ctx, vert_data=vert_data, @@ -226,6 +226,7 @@ class VShaderWrapper(ShaderWrapper): render_primitive=render_primitive, code_replacements=code_replacements, ) + self.fill_canvas = VShaderWrapper.get_fill_canvas(self.ctx) def init_program_code(self) -> None: self.program_code = { @@ -356,12 +357,12 @@ class VShaderWrapper(ShaderWrapper): self.fill_vao.render() if apply_depth_test: + self.ctx.enable(moderngl.DEPTH_TEST) depth_tx_fbo.clear(1.0) depth_tx_fbo.use() gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE) gl.glBlendEquation(gl.GL_MIN) self.fill_depth_vao.render() - self.ctx.enable(moderngl.DEPTH_TEST) # Now add border, just taking the max alpha gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE) @@ -378,6 +379,79 @@ class VShaderWrapper(ShaderWrapper): # Return to original blending state gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) + # Meant to be a static method returning one shared value across all VShaderWrappers + @lru_cache + @staticmethod + def get_fill_canvas(ctx: moderngl.Context) -> Tuple[Framebuffer, VertexArray, Framebuffer]: + """ + Because VMobjects with fill are rendered in a funny way, using + alpha blending to effectively compute the winding number around + each pixel, they need to be rendered to a separate texture, which + is then composited onto the ordinary frame buffer. + + This returns a texture, loaded into a frame buffer, and a vao + which can display that texture as a simple quad onto a screen, + along with the rgb value which is meant to be discarded. + """ + cam_config = get_configuration(parse_cli())['camera_config'] + size = (cam_config['pixel_width'], cam_config['pixel_height']) + + # Important to make sure dtype is floating point (not fixed point) + # so that alpha values can be negative and are not clipped + fill_texture = ctx.texture(size=size, components=4, dtype='f2') + # Use another one to keep track of depth + depth_texture = ctx.texture(size=size, components=1, dtype='f4') + + fill_texture_fbo = ctx.framebuffer(fill_texture) + depth_texture_fbo = ctx.framebuffer(depth_texture) + + simple_vert = ''' + #version 330 + + in vec2 texcoord; + out vec2 uv; + + void main() { + gl_Position = vec4((2.0 * texcoord - 1.0), 0.0, 1.0); + uv = texcoord; + } + ''' + alpha_adjust_frag = ''' + #version 330 + + uniform sampler2D Texture; + uniform sampler2D DepthTexture; + + in vec2 uv; + out vec4 color; + + void main() { + color = texture(Texture, uv); + if(color.a == 0) discard; + + // Counteract scaling in fill frag + color *= 1.06; + + gl_FragDepth = texture(DepthTexture, uv)[0]; + } + ''' + fill_program = ctx.program( + vertex_shader=simple_vert, + fragment_shader=alpha_adjust_frag, + ) + + fill_program['Texture'].value = get_texture_id(fill_texture) + fill_program['DepthTexture'].value = get_texture_id(depth_texture) + + verts = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) + simple_vbo = ctx.buffer(verts.astype('f4').tobytes()) + fill_texture_vao = ctx.simple_vertex_array( + fill_program, simple_vbo, 'texcoord', + mode=moderngl.TRIANGLE_STRIP + ) + + return (fill_texture_fbo, fill_texture_vao, depth_texture_fbo) + def render(self): if self.stroke_behind: self.render_stroke() diff --git a/manimlib/utils/shaders.py b/manimlib/utils/shaders.py index a8d1c3bb..e3c0c16f 100644 --- a/manimlib/utils/shaders.py +++ b/manimlib/utils/shaders.py @@ -7,8 +7,6 @@ import moderngl from PIL import Image import numpy as np -from manimlib.config import parse_cli -from manimlib.config import get_configuration from manimlib.utils.directories import get_shader_dir from manimlib.utils.file_ops import find_file @@ -133,74 +131,3 @@ def get_colormap_code(rgb_list: Sequence[float]) -> str: for rgb in rgb_list ) return f"vec3[{len(rgb_list)}]({data})" - - -@lru_cache() -def get_fill_canvas(ctx: moderngl.Context) -> Tuple[Framebuffer, VertexArray]: - """ - Because VMobjects with fill are rendered in a funny way, using - alpha blending to effectively compute the winding number around - each pixel, they need to be rendered to a separate texture, which - is then composited onto the ordinary frame buffer. - - This returns a texture, loaded into a frame buffer, and a vao - which can display that texture as a simple quad onto a screen, - along with the rgb value which is meant to be discarded. - """ - cam_config = get_configuration(parse_cli())['camera_config'] - size = (cam_config['pixel_width'], cam_config['pixel_height']) - - # Important to make sure dtype is floating point (not fixed point) - # so that alpha values can be negative and are not clipped - fill_texture = ctx.texture(size=size, components=4, dtype='f2') - # Use another one to keep track of depth - depth_texture = ctx.texture(size=size, components=1, dtype='f4') - - fill_texture_fbo = ctx.framebuffer(fill_texture) - depth_texture_fbo = ctx.framebuffer(depth_texture) - - simple_vert = ''' - #version 330 - - in vec2 texcoord; - out vec2 uv; - - void main() { - gl_Position = vec4((2.0 * texcoord - 1.0), 0.0, 1.0); - uv = texcoord; - } - ''' - alpha_adjust_frag = ''' - #version 330 - - uniform sampler2D Texture; - uniform sampler2D DepthTexture; - - in vec2 uv; - out vec4 color; - - void main() { - color = texture(Texture, uv); - if(color.a == 0) discard; - - // Counteract scaling in fill frag - color *= 1.06; - - gl_FragDepth = texture(DepthTexture, uv)[0]; - } - ''' - fill_program = ctx.program( - vertex_shader=simple_vert, - fragment_shader=alpha_adjust_frag, - ) - - fill_program['Texture'].value = get_texture_id(fill_texture) - fill_program['DepthTexture'].value = get_texture_id(depth_texture) - - verts = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) - simple_vbo = ctx.buffer(verts.astype('f4').tobytes()) - fill_texture_vao = ctx.simple_vertex_array( - fill_program, simple_vbo, 'texcoord', - mode=moderngl.TRIANGLE_STRIP - ) - return (fill_texture_fbo, fill_texture_vao, depth_texture_fbo)