与前景混合而不是背景

我想在场景的顶部绘制一些非常透明的物体,并使它们与场景中的其他像素很好地融合,但不能与背景混合; 如果我在(黑色)背景上画一个半透明物体,我不希望它被污染和画黑。

在这里输入图像描述

顶级场景是我得到的 – 顶部的半透明橙色三角形与黑色背景混合。 底部的场景是我想要的 – 半透明的橙色三角形只与蓝色三角形混合,但不与黑色背景混合。

这怎么能最好的实现? 有没有一个简单的混合function,这样做? 我想避免延期渲染,如果我可以的话。

有没有什么办法可以用gl.blendFuncgl.blendFuncSeparate来实现呢?

要建立肖恩的答案,你不需要做一个单独的FBO来渲染你正在寻找的效果。 不幸的是,你不能一次完成,因为你要求对像素进行两种不同的混合操作,这取决于你的橙色三角形是否与浅蓝色的三角形重叠。 幸运的是,这是一个可以使用模板缓冲区的好例子。

为了产生你想要的效果,你需要做以下的事情:

  1. 清除帧缓冲区的模板和颜色位。
  2. 将蓝色三角形渲染到颜色缓冲区和模板缓冲区中。
  3. 无论模板值是否为非零,使用常规的alpha混合testing渲染橙色三角形一次
  4. 在模板值为零的任何地方,再次渲染橙色三角形而不进行alpha混合(画家algorithm)。

您将需要确保您的OpenGL上下文有一个模板缓冲区,在上下文的创建过程中您通常可以执行此操作。 我相信WebGL明确要求。

这里还有一些(未经testing的)伪代码:

 // Enable both color and stencil buffers glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glEnable(GL_STENCIL_TEST); glStencilMask(0xFF); // Tell OpenGL to never pass the stencil test, and set the reference value to one. glStencilFunc(GL_NEVER, 1, 0xFF); // Tell OpenGL to replace the value in the stencil buffer with the reference value // whenever the stencil test fails (which is always) glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); // Clear your framebuffer glClearColor(0, 0, 0, 0); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); DrawBackgroundTriangle(); // Set the stencil function to only draw against non-zero values and to not modify // the stencil buffer on failure glStencilFunc(GL_NOTEQUAL, 0, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Enable alpha blending as usual glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); DrawForegroundTriangle(); // Set the stencil buffer to only draw against zero values and to not modify the // stencil buffer on failure. We do not need to call glStencilOp again because // the arguments will be the same. glStencilFunc(GL_EQUAL, 0, 0xFF); // Disable blending glDisable(GL_BLEND); DrawForegroundTriangle(); 

把山姆的评论变成一个答案,你需要通过两遍来做所有的事情。

对于第一遍,仅将前景对象绘制到离屏缓冲区/ FBO中。 如果你使用乘法混合,你可能会得到你想要的效果与白色背景; 与添加剂混合,你可能想要一个黑色的背景。 记得清理并更新alpha通道; 你可能想清除它到0。

然后,您可以再次执行渲染背景和完成的前景场景。 前景缓冲区在任何没有前景物体的地方都应该有0的alpha值,所以当前景缓冲区/纹理在背景上渲染时,你可以做一个简单的混合(甚至只是一个alphatesting)。

在GL中,psueodocode将是类似的(我不记得GL函数的名称或参数;必要时正确):

 setup: fg_buffer = glGenTexture() glBindTexture(fg_buffer) glTexImage2D(size_and_format_crud) glBindTexture(0) fg_fbo = glGenFBO() glBindFrameBuffer(fg_fbo) glAttachColorBuffer(fg_texture) glBindFrameBuffer(0) render_foreground: glBindFrameBuffer(fg_fbo) glClearColor(0, 0, 0, 0) glClear() setup_blend_and_draw() glBindFrameBuffer(0) render_scene: render_background() render_foreground() glBindTexture(fg_buffer) glDraw(full_screen_quad) glBindTexture(0)