我正在使用平铺渲染(使用glReadPixels)设置来截取场景的截图。我的输出看起来是正确的:
但是看着alpha通道,即使透明纹理渲染在不透明纹理之上,像素仍然是透明的。
特别是汽车的“内部”在我们看到天花板时应该是完全不透明的,但在穿过后窗时应该是部分透明的。
是否有一种方法可以测试像素上每个纹理的alpha分量,而不仅仅是最近的一个?
答案 0 :(得分:2)
是否有一种方法可以测试像素上每个纹理的alpha分量,而不仅仅是最近的一个?
没有。 OpenGL为每个像素存储一个RGBA值;没有办法获得以前的值(因为那需要大量的RAM)。
将哪些alpha值写入帧缓冲区取决于您的Alpha混合等式,您可以使用glBlendFunc
或glBlendFuncSeparate
进行设置。有关详细信息,请参阅OpenGL wiki上的blending page,this JavaScript app可让您查看各种混合模式的效果。
答案 1 :(得分:2)
我认为你从评论和其他答案中得到了一些有用的指导,但没有详细解决方案。让我走过去,而不是仅仅给出结果,这样你就知道下次自己如何解决这个问题了。
我假设你使用GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
混合功能将半透明物体画回到前面。你没有直接提到它,但它是相当标准的,并且与你所看到的一致。正确的解决方案还需要帧缓冲区中的alpha组件,但是你已经拥有了它,否则在读回alpha时你什么也得不到。
为了说明整个过程,我将在路上使用两个例子。我只会列出颜色的(R, A)
组件,G
和B
的行为与R
类似。
(R1, 1.0)
的图层,然后在其上方添加一个(R2, 0.4)
的图层。(R1, 0.5)
的图层,然后在其上方添加一个(R2, 0.4)
的图层。背景颜色为(Rb, 0.0)
,您总是希望清除这种混合的alpha值为0.0。
首先,让我们计算我们想要为颜色实现的结果:
对于案例1,绘制第一层完全覆盖背景,因为它具有alpha = 1.0。然后我们将第二层混合在它上面。由于它的alpha = 0.4,我们保留第一层的60%,并添加第二层的40%。所以我们想要的颜色是
0.6 * R1 + 0.4 * R2
对于案例1,绘制第一层保留50%的背景背景,因为它具有alpha = 0.5。所以到目前为止的颜色是
0.5 * Rb + 0.5 * R1
然后我们将第二层混合在它上面。我们再次保留前一种颜色的60%,并添加第二层的40%。所以我们想要的颜色是
0.6 * (0.5 * Rb + 0.5 * R1) + 0.4 * R2
= 0.3 * Rb + 0.3 * R1 + 0.4 * R2
现在,让我们弄清楚我们想要alpha的结果是什么:
对于案例1,我们的第一层完全不透明。观察不透明度的一种方法是测量物体吸收的光分数。一旦我们有一个吸收所有光线的层,我们渲染的任何其他东西都不会改变它。我们的总alpha应该是
1.0
对于案例2,我们有一层吸收50%的光,另一层吸收40%的剩余光。由于50%的40%是20%,因此总共吸收了70%。我们的总alpha应该是
0.7
使用GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
进行混合可为您提供所需的颜色效果。但正如你所注意到的,不是为了阿尔法。对示例进行计算:
案例1:绘制第1层,SRC_ALPHA
为1.0,源值为S = (R1, 1.0)
,目标值为D = (Rb, 0.0)
。因此,混合函数的计算结果为
SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D
= 1.0 * (R1, 1.0) + 0.0 * (Rb, 0.0)
= (R1, 1.0)
将其写入帧缓冲区,并成为绘制第2层的目标值。第2层的源为(R2, 0.4)
。使用SRC_ALPHA
进行评估
SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D
= 0.4 * (R2, 0.4) + 0.6 * (R1, 1.0)
= (0.4 * R2 + 0.6 * R1, 0.76)
案例2:绘制第1层,SRC_ALPHA
为0.5,源值为S = (R1, 0.5)
,目标值为D = (Rb, 0.0)
。因此,混合函数的计算结果为
SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D
= 0.5 * (R1, 0.5) + 0.5 * (Rb, 0.0)
= (0.5 * R1 + 0.5 * Rb, 0.25)
。
将其写入帧缓冲区,并成为绘制第2层的目标值。第2层的源为(R2, 0.4)
。使用SRC_ALPHA
进行评估
SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D
= 0.4 * (R2, 0.4) + 0.6 * (0.5 * R1 + 0.5 * Rb, 0.25)
= (0.4 * R2 + 0.3 * R1 + 0.3 * Rb, 0.31)
。
所以我们确认了你已经知道的东西:我们得到了所需的颜色,但错误的alphas。我们如何解决这个问题?我们需要一个不同的alpha混合函数。幸运的是,OpenGL有glBlendFuncSeparate()
,这使我们可以做到这一点。我们需要弄清楚的是用于alpha的混合函数。这是思考过程:
假设我们已经渲染了一些半透明物体,其总alpha值为A1
,它存储在帧缓冲区中。到目前为止我们渲染的内容会吸收总光线的一小部分A1
,然后让一部分1.0 - A1
通过。我们在其上面渲染另一个带有A2
的图层。该层吸收之前通过的光的一部分A2
,因此它吸收了所有光的额外(1.0 - A1) * A2
。我们需要将其添加到已吸收的光量中,以便现在总共吸收(1.0 - A1) * A2 + A1
。
剩下要做的就是将其转化为OpenGL混合方程式。 A2
是源值S
,A1
是目标值D
。所以我们想要的alpha结果变成
(1.0 - A1) * A2 + A1
= (1.0 - A1) * S + 1.0 * D
我称之为A1
的是帧缓冲区中的alpha值,在混合函数规范中称为DST_ALPHA
。因此,我们使用ONE_MINUS_DST_ALPHA
来匹配1.0 - A1
的源倍增器。我们使用GL_ONE
来匹配目标乘数1.0
。
因此alpha的混合函数参数为(GL_ONE_MINUS_DST_ALPHA, GL_ONE)
,完整的混合函数调用为:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE_MINUS_DST_ALPHA, GL_ONE);
我们可以再次用示例再次检查alpha的数学运算:
案例1:绘制第1层,DST_ALPHA
为0.0,源值为S = (.., 1.0)
,目标值为D = (.., 0.0)
。因此,混合函数的计算结果为
ONE_MINUS_DST_ALPHA * S + ONE * D
= 1.0 * (.., 1.0) + 1.0 * (.., 0.0)
= (.., 1.0)
这将写入帧缓冲区,并成为绘制第2层的目标值。第2层的源是(.., 0.4)
,DST_ALPHA
现在是1.0。评估第2层的混合方程式
ONE_MINUS_DST_ALPHA * S + ONE * D
= 0.0 * (.., 0.4) + 1.0 * (.., 1.0)
= (.., 1.0)
我们得到了所需的alpha值1.0
!
案例2:绘制第1层,DST_ALPHA
为0.0,源值为S = (.., 0.5)
,目标值为D = (.., 0.0)
。因此,混合函数的计算结果为
ONE_MINUS_DST_ALPHA * S + ONE * D
= 1.0 * (.., 0.5) + 1.0 * (.., 0.0)
= (.., 0.5)
将其写入帧缓冲区,并成为绘制第2层的目标值。第2层的源为(.., 0.4)
,DST_ALPHA
现为0.5。评估第2层的混合方程式
ONE_MINUS_DST_ALPHA * S + ONE * D
= 0.5 * (.., 0.4) + 1.0 * (.., 0.5)
= (.., 0.7)
我们得到了所需的alpha值0.7
!