Pergunta

I'm trying to display a very simple thing (at least I thought so) with OpenGL ES 2.0 on Android: A monochrome quad with transparency. My fragment shader basically just does this:

gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);

So the quad should appear in pure red but with 50% transparency so that the background (black) comes through, which should make it a bit darker. However, the quad has exactly the same appearance, no matter what value I choose for the alpha channel element in the vector. Even if I set alpha to 0.0 it is drawn in pure red.

I switched on GL_BLEND. Here are my initializations:

GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);        
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);

The draw function is as simple as it could be:

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
shader.use(); // will select the vertex and fragment shader
GLES20.glViewport(0, 0, resX, resY); // sets the viewport to the current resolution
GLES20.glEnableVertexAttribArray(v_aPosId);
GLES20.glVertexAttribPointer(v_aPosId, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 0, vertexBuf);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(v_aPosId);

The surface view is created like this:

glView = new GLSurfaceView(this);
glView.setEGLContextClientVersion(2);
glView.setRenderer(new GLRenderer(this));

How comes the alpha channel is just ignored? Did I miss switching something on or off? Used hardware: Google Nexus 10 with Android 4.2.1.

Edit: When I used glReadPixels() to read back the framebuffer, the RGBA pixel values were also 255, 0, 0, 255, and not 255, 0, 0, 127 as it should be. However I fixed it. If you want to display transparency correctly, see @martijn-courteaux's answer. If you want to get the correct pixel values back (as via glReadPixels()) see my answer.

Foi útil?

Solução

When choosing the glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); blending function, you will have to premultiply your color. So, this means you will have to multiply every color component (red, green and blue) by the alpha value. In your case this would be: (1.0 * alpha, 0.0, 0.0, alpha). The blending function describes how the GPU will compute the final color as the result of the two blended colors. If you choose GL_ONE, GL_ONE_MINUS_SRC_ALPHA, you have this formula:

finalColor = srcColor + (1 - srcAlpha) * dstColor;

If you fill in your red color, you see:

finalColor = (1.0, 0.0, 0.0) + (1 - 0.5) * (0.0, 0.0, 0.0)
           = (1.0, 0.0, 0.0)

That is why it looks pure red.

If you don't like that, then change the blending function to:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); If you use this function, the formula gets obviously:

finalColor = srcAlpha * srcColor + (1 - srcAlpha) * dstColor;

Plug in your red:

finalColor = 0.5 * (1.0, 0.0, 0.0) + (1.0 - 0.5) * (0.0, 0.0, 0.0)
           = (0.5, 0.0, 0.0)

Note that premultiplying is good when you are working with images: it saves the GPU three multiplication per fragment.

Outras dicas

I found it out. @martijn-courteaux's answer (glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)) is perfectly correct regarding displaying the quad with correct transparency. However, I was trying to achieve something different that I did not mention yet. I didn't just want to display the quad correctly but wanted to get the correct RGBA pixel values of my generated image using glReadPixels(). When you want to do this, you need to choose the right EGL config in your GLSurfaceView by calling setEGLConfigChooser(8, 8, 8, 8, 0, 0). Note the 4th "8". The android documentation says "by default the view will choose an RGB_888 surface" so I had to set the alpha channel to 8, too. Now you have to disable blending via glDisable(GL_BLEND). Now the GL surface will draw the quad without transparency, but if you use glReadPixels() it will return RGBA pixel values 255, 0, 0, 127 as it should for the above fragment shader. This is useful if you're generating images offscreen on the GPU.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top