mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Ensure border width blends better within filled VMobject for opacity < 1
This commit is contained in:
parent
7217c9fca5
commit
6223623b40
2 changed files with 60 additions and 21 deletions
|
@ -325,15 +325,17 @@ class VShaderWrapper(ShaderWrapper):
|
|||
return
|
||||
|
||||
original_fbo = self.ctx.fbo
|
||||
texture_fbo, depth_texture_fbo, texture_vao = self.fill_canvas
|
||||
fill_tx_fbo, fill_tx_vao, border_tx_fbo, border_tx_vao, depth_tx_fbo = self.fill_canvas
|
||||
|
||||
# First, draw the border for antialiasing
|
||||
border_tx_fbo.clear()
|
||||
border_tx_fbo.use()
|
||||
self.fill_border_vao.render()
|
||||
|
||||
# Render to a separate texture, due to strange alpha compositing
|
||||
# for the blended winding calculation
|
||||
texture_fbo.clear()
|
||||
texture_fbo.use()
|
||||
fill_tx_fbo.clear()
|
||||
fill_tx_fbo.use()
|
||||
|
||||
# Be sure not to apply depth test while rendering fill
|
||||
# but set it back to where it was after
|
||||
|
@ -341,30 +343,34 @@ class VShaderWrapper(ShaderWrapper):
|
|||
|
||||
self.ctx.disable(moderngl.DEPTH_TEST)
|
||||
gl.glBlendFuncSeparate(
|
||||
# Ordinary blending for colors
|
||||
gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA,
|
||||
# The effect of blending with -a / (1 - a)
|
||||
# should be to cancel out
|
||||
gl.GL_ONE_MINUS_DST_ALPHA, gl.GL_ONE,
|
||||
# With this blend function, the effect of blending alpha a with
|
||||
# -a / (1 - a) cancels out, so we can cancel positively and negatively
|
||||
# oriented triangles
|
||||
gl.GL_ONE_MINUS_DST_ALPHA, gl.GL_ONE
|
||||
)
|
||||
|
||||
self.fill_vao.render()
|
||||
|
||||
if apply_depth_test:
|
||||
depth_texture_fbo.clear(1.0)
|
||||
depth_texture_fbo.use()
|
||||
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)
|
||||
|
||||
# Render fill onto the border_width fbo
|
||||
# two alphas, before compositing back to the rest of the scene
|
||||
border_tx_fbo.use()
|
||||
gl.glEnable(gl.GL_BLEND)
|
||||
gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE)
|
||||
gl.glBlendEquation(gl.GL_MAX)
|
||||
fill_tx_vao.render()
|
||||
|
||||
original_fbo.use()
|
||||
gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
|
||||
gl.glBlendEquation(gl.GL_FUNC_ADD)
|
||||
texture_vao.render()
|
||||
|
||||
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
|
||||
gl.glBlendEquation(gl.GL_FUNC_ADD)
|
||||
border_tx_vao.render()
|
||||
|
||||
def render(self):
|
||||
if self.stroke_behind:
|
||||
|
|
|
@ -152,9 +152,14 @@ def get_fill_canvas(ctx: moderngl.Context) -> Tuple[Framebuffer, VertexArray]:
|
|||
|
||||
# Important to make sure dtype is floating point (not fixed point)
|
||||
# so that alpha values can be negative and are not clipped
|
||||
texture = ctx.texture(size=size, components=4, dtype='f2')
|
||||
fill_texture = ctx.texture(size=size, components=4, dtype='f2')
|
||||
# Use a separate texture to firset render the antialiased border
|
||||
border_texture = ctx.texture(size=size, components=4, dtype='f1')
|
||||
# Use yet another one to keep track of depth
|
||||
depth_texture = ctx.texture(size=size, components=1, dtype='f4')
|
||||
texture_fbo = ctx.framebuffer(texture)
|
||||
|
||||
fill_texture_fbo = ctx.framebuffer(fill_texture)
|
||||
border_texture_fbo = ctx.framebuffer(border_texture)
|
||||
depth_texture_fbo = ctx.framebuffer(depth_texture)
|
||||
|
||||
simple_vert = '''
|
||||
|
@ -183,22 +188,50 @@ def get_fill_canvas(ctx: moderngl.Context) -> Tuple[Framebuffer, VertexArray]:
|
|||
|
||||
// Counteract scaling in fill frag
|
||||
color.a *= 1.06;
|
||||
// Cancel out what was effectively a premultiplication
|
||||
color.rgb /= color.a;
|
||||
|
||||
gl_FragDepth = texture(DepthTexture, uv)[0];
|
||||
}
|
||||
'''
|
||||
simple_program = ctx.program(
|
||||
simple_frag = '''
|
||||
#version 330
|
||||
|
||||
uniform sampler2D Texture;
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = texture(Texture, uv);
|
||||
if(color.a == 0) discard;
|
||||
}
|
||||
'''
|
||||
fill_program = ctx.program(
|
||||
vertex_shader=simple_vert,
|
||||
fragment_shader=alpha_adjust_frag,
|
||||
)
|
||||
border_program = ctx.program(
|
||||
vertex_shader=simple_vert,
|
||||
fragment_shader=simple_frag,
|
||||
)
|
||||
|
||||
simple_program['Texture'].value = get_texture_id(texture)
|
||||
simple_program['DepthTexture'].value = get_texture_id(depth_texture)
|
||||
fill_program['Texture'].value = get_texture_id(fill_texture)
|
||||
fill_program['DepthTexture'].value = get_texture_id(depth_texture)
|
||||
border_program['Texture'].value = get_texture_id(border_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(
|
||||
simple_program, simple_vbo, 'texcoord',
|
||||
fill_program, simple_vbo, 'texcoord',
|
||||
mode=moderngl.TRIANGLE_STRIP
|
||||
)
|
||||
return (texture_fbo, depth_texture_fbo, fill_texture_vao)
|
||||
border_texture_vao = ctx.simple_vertex_array(
|
||||
border_program, simple_vbo, 'texcoord',
|
||||
mode=moderngl.TRIANGLE_STRIP
|
||||
)
|
||||
return (
|
||||
fill_texture_fbo, fill_texture_vao,
|
||||
border_texture_fbo, border_texture_vao,
|
||||
depth_texture_fbo,
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue