Home » Android » android – OpenGL ES 2.0 PNG alpha channel

android – OpenGL ES 2.0 PNG alpha channel

Posted by: admin June 15, 2020 Leave a comment

Questions:

I am just learning to work with OpenGL ES 2.0 for Android. I have been trying to simply display a texture on the middle of the screen, which was easy enough, but I cannot seem to get the PNG alpha to work properly. The image will either show with a black background, or the entire image will be blended into the background color slightly, depending on the settings I use.

The actual tutorials I have followed to get to this point never worked with transparency, so I have tried to work in code I have found by searching around, and likely have just missed one important step. I have searched quite a lot to figure this problem out though, and I have not seen any answers that had something my setup does not. I have tried every combination of glBlendFunc and what not with no luck.

I figured if I tried to paste in all of the code that may be related to this, the question would seem very bloated, so I will be happy to post any bits of code you guys ask for. I’d greatly appreciate any ideas for what I should try next.

EDIT :: Here is my fragment shader, which is what I believe to be the cause. This is the only part that I have never really found a decent example for working with transparency, and everything else matches what I have seen elsewhere.

        final String fragmentShader =           
        "precision mediump float;       \n"  
        + "varying vec2 v_Color;          \n"     
        + "uniform sampler2D s_baseMap;   \n"
        + "void main()                    \n"     
        + "{                              \n"
        + "  vec4 baseColor;              \n"
        + "  baseColor = texture2D( s_baseMap, v_Color );   \n"
        + "   gl_FragColor = baseColor;     \n"    
        + "}                              \n"; 

It never does anything with the alpha explicitly, it is from an example that doesn’t use it after all, but I still don’t know much about fragment shaders and because it seemed to “sort of” work when it blends the image into the background, I figured it was working with the alpha in some form and I just had something set wrong.

EDIT :: Here is the “loadTexture” method. It is roughly the same as the example from the openGL ES 2.0 book that I am trying to learn from, with a few alterations that seem to get the image closer to working properly.

    private int loadTexture ( InputStream is )
{
    int[] textureId = new int[1];
    Bitmap bitmap;
    bitmap = BitmapFactory.decodeStream(is);
    byte[] buffer = new byte[bitmap.getWidth() * bitmap.getHeight() * 4];

    for ( int y = 0; y < bitmap.getHeight(); y++ )
        for ( int x = 0; x < bitmap.getWidth(); x++ )
        {
            int pixel = bitmap.getPixel(x, y);
            buffer[(y * bitmap.getWidth() + x) * 4 + 0] = (byte)((pixel >> 16) & 0xFF);
            buffer[(y * bitmap.getWidth() + x) * 4 + 1] = (byte)((pixel >> 8) & 0xFF);
            buffer[(y * bitmap.getWidth() + x) * 4 + 2] = (byte)((pixel >> 0) & 0xFF);
        }

    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bitmap.getWidth() * bitmap.getHeight() * 4);
    byteBuffer.put(buffer).position(0);

    GLES20.glGenTextures ( 1, textureId, 0 );
    GLES20.glBindTexture ( GLES20.GL_TEXTURE_2D, textureId[0] );

    GLES20.glTexImage2D ( GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(), 0, 
                          GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuffer );

    GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR );
    GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR );
    GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE );
    GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE );

    return textureId[0];
}

I understand what the code is doing, but admittedly it still confuses me a bit so I may just be missing something obvious due to my lack of knowledge.

I don’t see any other parts of my code that seem like they could cause the kind of problems I am having, but programming is always full of the unexpected (Especially in the world of OpenGL it seems) so if you think something else is the cause I’ll be sure to post that for you as well. Sorry for all the trouble!

How to&Answers:

Change

 for ( int y = 0; y < bitmap.getHeight(); y++ )
    for ( int x = 0; x < bitmap.getWidth(); x++ )
    {
        int pixel = bitmap.getPixel(x, y);
        buffer[(y * bitmap.getWidth() + x) * 4 + 0] = (byte)((pixel >> 16) & 0xFF);
        buffer[(y * bitmap.getWidth() + x) * 4 + 1] = (byte)((pixel >> 8) & 0xFF);
        buffer[(y * bitmap.getWidth() + x) * 4 + 2] = (byte)((pixel >> 0) & 0xFF);
    }

into

 for ( int y = 0; y < bitmap.getHeight(); y++ )
    for ( int x = 0; x < bitmap.getWidth(); x++ )
    {
        int pixel = bitmap.getPixel(x, y);
        buffer[(y * bitmap.getWidth() + x) * 4 + 0] = (byte)((pixel >> 16) & 0xFF);
        buffer[(y * bitmap.getWidth() + x) * 4 + 1] = (byte)((pixel >> 8) & 0xFF);
        buffer[(y * bitmap.getWidth() + x) * 4 + 2] = (byte)((pixel >> 0) & 0xFF);
        buffer[(y * bitmap.getWidth() + x) * 4 + 3] = (byte)((pixel >> 24) & 0xFF);
    }

to include the alpha information then simply add

GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND); 

right before you draw the texture. Remember to disable GL_BLEND once you are done.

Answer:

You most likely want to use glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) as the blending function and you have to make sure to also write the alpha value of the texture to the gl_FragColor fragment shader output variable.

For all that to work your uploaded texture data has to contain an alpha value and you must have used a texture format that supports an alpha channel (RGBA, RGBA8 etc.).

You could verify this by simply routing the alpha value to the RGB color components and inspecting the image that you get.

EDIT:

In your image loading code you forget to copy over the alpha channel! Try the suggestion that davebytes gives.

Answer:

your initial shader is fine. alpha is inherent in color ops, it just may not be applied depending on blend/mode/etc.

given that your texture comes out black if you do fragcolor = base.aaa, that implies your texture data is ‘bad’.

looking at your texture load, yeah, it’s wrong. you never copy over the alpha, just the rgb. assuming java clears the byte array to 0s, all alpha will be zero, that would get you your black box, that would cause the image to ‘vanish’ when you enable alpha blending.

To simplify your life, instead of all the hand copying and stuff, you can simply load the bitmap normally and use the GLUtils helper to upload instead of directly using glTexImage2d:

  bitmap = BitmapFactory.decodeStream(is);  
  GLES20.glGenTextures ( 1, textureId, 0 );
  GLES20.glBindTexture ( GLES20.GL_TEXTURE_2D, textureId[0] );
  GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

Something like that. Then enable blending, use src+invsrc blend mode if not premultiplied, and render, you should get the result desired.