本文介绍了OpenGL ReadPixels(屏幕截图)Alpha的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用图块渲染(使用glReadPixels)设置来拍摄场景的屏幕截图。我的输出看起来正确:





但是从alpha通道来看,即使透明纹理是在不透明纹理之上渲染的,像素仍然是透明的。





尤其是汽车的内部应该在我们看到天花板的地方完全不透明,但是在通过后窗时部分透明。



有没有一种方法可以测试每个纹理的像素的像素,而不仅仅是最接近的像素?

解决方案

我认为您从注释和其他答案中获得了一些有用的指导,但未提供详细的解决方案。我不只是给出结果,而是让我逐步了解它,以便您下次知道如何解决这个问题。



我假设您绘制的是半透明的使用 GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA 混合函数将对象放回最前面。您没有直接提及它,但这是相当标准的,并且与您所看到的一致。正确的解决方案还将在帧缓冲区中需要一个alpha组件,但是您已经拥有了它,否则在读回alpha时您将一无所获。



为说明整个过程,在此过程中,我将使用两个示例。我将只列出颜色的(R,A)组件, G B 的行为类似于 R


  1. 绘制颜色为(R1,1.0)的图层,然后是(R2,0.4)的图层。

  2. 绘制颜色为(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




  • 0.7




使用 GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA 进行混合可为您提供所需的颜色结果。但是正如您所注意到的,不是针对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)。用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)。用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)




所以我们确认了您已经知道的内容:我们得到了所需的颜色,但是alpha值错误。我们该如何解决?我们需要一个不同的alpha混合功能。幸运的是,OpenGL具有 glBlendFuncSeparate(),这使我们能够做到这一点。我们需要弄清楚的是用于alpha的混合功能。这是一个思考过程:



比方说,我们已经渲染了一些半透明的对象,它们的总alpha为 A1 ,它存储在帧缓冲区中。到目前为止,我们渲染的图像吸收了总光线的一部分 A1 ,并使一部分 1.0-A1 通过。我们渲染另一个在其顶部带有alpha 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值在blend函数规范中称为 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



I'm using a Tile Rendering (using glReadPixels) setup to take screenshots of a scene. My output looks correct:

But looking at the alpha channel the pixels are still transparent even if the transparent texture is rendered on top of an opaque texture.

In particular the "inside" of the car should be completely opaque where we see the ceiling, but partially transparent when going through the back windows.

Is there a method for testing the alpha component of each texture at a pixel, not just the closest one?

解决方案

I think you got some useful direction from the comments and the other answer, but not the solution in full detail. Instead of just giving the result, let me walk through it, so that you know how to figure it out yourself next time.

I'm assuming that you draw your translucent objects back to front, using a GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA blend function. You don't directly mention that, but it's fairly standard, and consistent with what you're seeing. The correct solution will also need an alpha component in the framebuffer, but you have that already, otherwise you would get nothing when reading back the alpha.

To illustrate the whole process, I'm going to use two examples on the way. I'll only list the (R, A) components for the colors, G and B would behave just like R.

  1. Draw a layer with color (R1, 1.0), then a layer with (R2, 0.4) on top of it.
  2. Draw a layer with color (R1, 0.5), then a layer with (R2, 0.4) on top of it.

The background color is (Rb, 0.0), you always want to clear with an alpha value of 0.0 for this kind of blending.

First, let's calculate the result we want to achieve for the colors:

  • For case 1, drawing the first layer completely covers the background, since it has alpha = 1.0. Then we blend the second layer on top of it. Since it has alpha = 0.4, we keep 60% of the first layer, and add 40% of the second layer. So the color we want is

    0.6 * R1 + 0.4 * R2

  • For case 1, drawing the first layer keeps 50% of the background background, since it has alpha = 0.5. So the color so far is

    0.5 * Rb + 0.5 * R1

    Then we blend the second layer on top of it. Again we keep 60% of the previous color, and add 40% of the second layer. So the color we want is

    0.6 * (0.5 * Rb + 0.5 * R1) + 0.4 * R2= 0.3 * Rb + 0.3 * R1 + 0.4 * R2

Now, let's figure out what we want the result for alpha to be:

  • For case 1, our first layer was completely opaque. One way of looking at opacity is as a measure of what fraction of light is absorbed by the object. Once we have a layer that absorbs all the light, anything else we render will not change that. Our total alpha should be

    1.0

  • For case 2, we have one layer that absorbs 50% of the light, and one that absorbs 40% of the remaining light. Since 40% of 50% is 20%, a total of 70% is absorbed. Our total alpha should be

    0.7

Using GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA for blending gives you the desired result for the color. But as you noticed, not for alpha. Doing the calculation on the example:

  • Case 1: Drawing layer 1, SRC_ALPHA is 1.0, the source value is S = (R1, 1.0) and the destination value is D = (Rb, 0.0). So the blend function evaluates as

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D= 1.0 * (R1, 1.0) + 0.0 * (Rb, 0.0)= (R1, 1.0)

    This is written to the framebuffer, and becomes the destination value for drawing layer 2. The source for layer 2 is (R2, 0.4). Evaluating with 0.4 for SRC_ALPHA gives

    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)

  • Case 2: Drawing layer 1, SRC_ALPHA is 0.5, the source value is S = (R1, 0.5) and the destination value is D = (Rb, 0.0). So the blend function evaluates as

    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).

    This is written to the framebuffer, and becomes the destination value for drawing layer 2. The source for layer 2 is (R2, 0.4). Evaluating with 0.4 for SRC_ALPHA gives

    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).

So we confirmed what you already knew: We get the desired colors, but the wrong alphas. How do we fix this? We need a different blend function for alpha. Fortunately OpenGL has glBlendFuncSeparate(), which allows us to do exactly that. All we need to figure out is what blend function to use for alpha. Here is the thought process:

Let's say we already rendered some translucent objects, with a total alpha of A1, which is stored in the framebuffer. What we rendered so far absorbs a fraction A1 of the total light, and lets a fraction 1.0 - A1 pass through. We render another layer with alpha A2 on top of it. This layer absorbs a fraction A2 of the light that passed through before, so it absorbs an additional (1.0 - A1) * A2 of all the light. We need to add this to the amount of light that was already absorbed, so that a total of (1.0 - A1) * A2 + A1 is now absorbed.

All that's left to do is translate that into an OpenGL blend equation. A2 is the source value S, and A1 the destination value D. So our desired alpha result becomes

(1.0 - A1) * A2 + A1 = (1.0 - A1) * S + 1.0 * D

What I called A1 is the alpha value in the framebuffer, which is referred to as DST_ALPHA in the blend function specification. So we use ONE_MINUS_DST_ALPHA to match our source multiplier of 1.0 - A1. We use GL_ONE to match the destination multiplier 1.0.

So the blend function parameters for alpha are (GL_ONE_MINUS_DST_ALPHA, GL_ONE), and the complete blend function call is:

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE_MINUS_DST_ALPHA, GL_ONE);

We can double check the math for alpha with the examples one more time:

  • Case 1: Drawing layer 1, DST_ALPHA is 0.0, the source value is S = (.., 1.0) and the destination value is D = (.., 0.0). So the blend function evaluates as

    ONE_MINUS_DST_ALPHA * S + ONE * D= 1.0 * (.., 1.0) + 1.0 * (.., 0.0)= (.., 1.0)

    This is written to the framebuffer, and becomes the destination value for drawing layer 2. The source for layer 2 is (.., 0.4), and DST_ALPHA is now 1.0. Evaluating the blend equation for layer 2 gives

    ONE_MINUS_DST_ALPHA * S + ONE * D= 0.0 * (.., 0.4) + 1.0 * (.., 1.0)= (.., 1.0)

    We got the desired alpha value of 1.0!

  • Case 2: Drawing layer 1, DST_ALPHA is 0.0, the source value is S = (.., 0.5) and the destination value is D = (.., 0.0). So the blend function evaluates as

    ONE_MINUS_DST_ALPHA * S + ONE * D= 1.0 * (.., 0.5) + 1.0 * (.., 0.0)= (.., 0.5)

    This is written to the framebuffer, and becomes the destination value for drawing layer 2. The source for layer 2 is (.., 0.4), and DST_ALPHA is now 0.5. Evaluating the blend equation for layer 2 gives

    ONE_MINUS_DST_ALPHA * S + ONE * D= 0.5 * (.., 0.4) + 1.0 * (.., 0.5)= (.., 0.7)

    We got the desired alpha value of 0.7!

这篇关于OpenGL ReadPixels(屏幕截图)Alpha的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-18 00:25