mirror of
https://github.com/3b1b/manim.git
synced 2026-01-10 15:08:16 -05:00
Move get_fill_canvas into VShaderWrapper
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user