glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
我经常用OpenGL发现,当我尝试在纹理上绘制一些东西,然后在另一个表面上绘制时,使用靠近其边缘的图像。这显然是由于制作本质上是由这种混合风格引起的预乘 alpha 的问题引起的。对于 3d 场景,我通常使用画家算法,而对于渲染 2d 内容,我喜欢先绘制最后面的对象,然后在它们上面叠加其他对象。值得注意的是,在进行这样的 2d 绘图时,我通常不能像在 3d 中那样依赖像 az buffer 这样的功能,这就是为什么渲染 guis 之类的东西比渲染 3d 场景图更成问题
我认为值得注意的是,当目标缓冲区恰好已经完全不透明时,上述混合风格实际上是完美的,但如果目标是透明的,那么在这种情况下真正需要的是glBlendFunc(GL_SRC,GL_ZERO)
......因为任何全白像素例如,在源中部分透明的应该保持与颜色一样完全饱和,而不是与实际不存在的背景“混合”,因为使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
实际上会渲染一个部分透明和全白的像素前景和背景之间的混合,这是我想要的,但不会丢失 alpha 信息,也不会导致颜色实际改变。
因此,在我看来,一种可能是完美的混合功能实际上是混合了由其他原因引起的glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
内容和由其他原因引起的内容,glBlendFunc(GL_SRC,GL_ZERO)
这取决于DST_ALPHA
自身的价值。
因此,理想情况下:
Cr = Cs * (1 - Ad) + (Cs * As + Cd * (1 - As)) * Ad
Ar = As * (1 - Ad) + Ad * Ad
这样,如果目标已经不透明,则正常的 alpha 乘法会适当地混合,而如果目标是透明的或部分透明的,它将使用更多源图像的真实颜色,而不是使用所述颜色的 alpha 预乘形式。
然而,似乎没有明显的方式来向 OpenGL 描述这种混合机制。似乎这些问题不应该是我的情况所独有的,而且在我看来,似乎没有任何方法可以实现这一点……即使在可编程管道中也是如此。
到目前为止,我发现的唯一解决方法是每当我绘制到稍后将在其他地方绘制的纹理时反转绘制顺序,并使用glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_SRC_ALPHA, GL_DST_ALPHA)
.
在我在纹理上绘制一些稍后将正常渲染的东西的情况下,为绘制顺序制作一个特殊情况是一种折衷方案,但这对我来说没有吸引力,因为它缺乏可重用性,除非总是让正在绘制的代码知道目的地的类型是什么,以便它可以颠倒自己通常的绘图顺序,我可以不必要地使它复杂化。
最后,虽然颠倒整个场景的绘制顺序不一定完全不可能,但它需要在绘制第一个对象之前确定将在每一帧图形中显示的对象的完整列表。我遇到的核心问题是,尽管在渲染 3D 场景时这可能是可持续的,但在绘制 2d gui 之类的东西时,绘制的东西种类繁多,绘制方式如此之多,以至于没有将它们概括为单个对象列表的方法,以后总是可以以相反的顺序遍历该对象列表。在我看来,对于如何在这种“倒退”中构建 2d 场景,它需要一种截然不同的观点
那么......还有其他方法可以做到这一点吗?我错过了什么吗?还是我必须学习一种完全不同的方式来构建二维图像?我不能是唯一遇到这个问题的人,但我还没有找到任何好的和干净的解决方案。
编辑:
如果确实存在由 NVIDIA 或其他方式对 OpenGL 的任何常用扩展,则允许使用可以执行此操作的混合模式:
Cr = Cs * (1 - Ad) + (Cs * As + Cd * (1 - As)) * Ad
Ar = As * (1 - Ad) + Ad * Ad
(其中 Cs 和 Cd 是源颜色和目标颜色,As 和 Ad 是源和目标 alpha 值,Cr 和 Ar 是结果颜色和 alpha 值),我真的很想知道扩展名是什么,以及如何使用它……只要它可以在消费硬件上生产。