opengl中图像的部分透明部分周围的暗晕

mar*_*964 8 opengl alphablending premultiplied-alpha

我经常用OpenGL找到,当我试图在纹理上绘制一些东西然后再到另一个表面上时,使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),我总是最终得到一个彩色光环(通常是一个深色的带),在我的源的半透明部分边缘附近的图像.这显然是由于制作本质上是由该混合风格引起的预乘alpha的问题引起的.对于3D场景,我通常使用画家算法,并且为了渲染2D内容,我喜欢先绘制最后面的对象,然后在它们之上覆盖其他对象.值得注意的是,在进行这样的2D绘制时,我通常不能像3d中那样依赖像az缓冲区这样的功能,这就是为什么渲染像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
Run Code Online (Sandbox Code Playgroud)

这样,如果目的地已经是不透明的,则正常的阿尔法乘法适当地混合,而如果目的地是透明的或部分透明的,它将使用更多的源图像的真实颜色而不是使用所述颜色的阿尔法预乘形式.

然而,似乎没有明显的方法来描述这种混合机制到OpenGL.似乎这些问题在我的情况下几乎不应该是独一无二的,而且对我来说似乎并不常见,即使在可编程管道中也没有任何方法可以实现这一点.

到目前为止,我发现的唯一解决方法是每当我绘制一个纹理时反转绘图顺序,我将在其他地方绘制并使用glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_SRC_ALPHA, GL_DST_ALPHA).

在我将在以后正常渲染的纹理上绘制某些东西的情况下绘制顺序的特殊情况是一种妥协,但这对我来说没有吸引力,因为它缺乏可重用性,除非执行绘图的代码总是让人知道什么类型的目的地,以便它可以扭转自己通常的绘图顺序,我可以毫不必要地使它复杂化.

最后,尽管反转整个场景的绘制顺序并不一定完全不可能,但它要求在绘制第一个对象之前确定将在每个图形帧中显示的完整对象列表.我对此有一个核心问题是虽然在渲染3D场景时这可能是可持续的,但在绘制像2d gui这样的东西时,绘制的各种各样的东西以及绘制它们的方式都是如此之大以至于没有将它们概括为单个对象列表的方法,该列表总是可以在以后以相反的顺序遍历.在我看来,它需要这样一个根本不同的观点,如何按照我习以为常的"倒退"顺序构建2D场景,我现在根本无法理解可维护的解决方案.

那么......有另一种方法吗?我错过了什么吗?或者我是否必须学习一种完全不同的方式来构建2D图像?我不可能是唯一有这个问题的人,但我还没有找到任何好的和干净的解决方案.

编辑:

如果确实存在OpenGL的任何常用扩展,通过NVIDIA或其他方式,允许混合模式可以执行此操作:

Cr = Cs * (1 - Ad) + (Cs * As + Cd * (1 - As)) * Ad
Ar = As * (1 - Ad) + Ad * Ad
Run Code Online (Sandbox Code Playgroud)

(其中Cs和Cd是源和目标颜色,As和Ad是源和目标alpha值,Cr和Ar是结果颜色和alpha值),我真的想知道扩展名被调用了什么,以及如何使用它...只要它可以在消费类硬件上生产.

tim*_*day 1

我写过各种将半透明 2D 叠加层合成到半透明 3D 内容上的文章,但从未glBlendFunc觉得 OpenGL 遗漏了/领域中的关键内容glBlendFuncSeparate(至少在您开始担心 sRGB 内容和帧缓冲区的伽玛校正合成之前不会)。

我主要怀疑你使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)明显的乘阿尔法(抱歉,虽然你的问题很长,但我实际上并没有发现它那么清楚;一些简单测试用例的图像可能会有所帮助)。对于合成预乘 alpha 内容,我希望看到 aGL_ONE作为参数之一glBlendFunc(哪一个取决于您是否进行过度/不足操作)。

这里有一个关于混合/合成的非常好的幻灯片(包括正确使用预乘颜色与“直接”颜色)(幻灯片 20-35 相关材料)。

(尽管我声称现有的功能总体上已经足够好了……还值得一提的是,NVidia 最近在该领域添加了一些新功能)。