OpenGL ES 2.0 PNG alpha通道

时间:2011-08-17 17:12:21

标签: android opengl-es png transparency alpha

我正在学习使用适用于Android的OpenGL ES 2.0。我一直试图在屏幕中间只显示一个纹理,这很容易,但我似乎无法让PNG alpha正常工作。图像将以黑色背景显示,或者整个图像将略微混合到背景颜色中,具体取决于我使用的设置。

为了达到这一点,我所遵循的实际教程从未使用透明度,因此我尝试使用搜索到的代码,并且可能只是错过了一个重要的步骤。我已经搜索了很多来解决这个问题,但我还没有看到任何答案,我的设置没有。我已经尝试了glBlendFunc的所有组合以及没有运气的组合。

我想如果我尝试粘贴可能与此相关的所有代码,问题看起来会非常臃肿,所以我很乐意发布你们要求的任何代码。我非常感谢我接下来应该尝试的任何想法。

编辑:: 这是我的片段着色器,这是我认为的原因。这是我从来没有真正找到一个体面的例子来处理透明度,而其他一切都与我在别处看到的一致。

        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"; 

它从未明确地对alpha做任何事情,它来自一个完全没有使用它的例子,但我仍然不太了解片段着色器,因为它似乎在“混合”时“有点”工作图像进入背景,我认为它是以某种形式使用alpha并且我只是设置了错误。

编辑:: 这是“loadTexture”方法。它与我试图从中学习的openGL ES 2.0书中的示例大致相同,只是进行了一些改动,使图像更接近正常工作。

    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];
}

我理解代码在做什么,但不可否认,它仍然让我感到困惑,所以我可能因为缺乏知识而遗漏了一些明显的东西。

我没有看到我的代码中的任何其他部分似乎可能导致我遇到的问题,但编程总是充满意外(特别是在OpenGL的世界中),所以如果你认为还有其他原因我也一定会为你发布。抱歉所有的麻烦!

3 个答案:

答案 0 :(得分:9)

更改

 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);
    }

 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);
    }

包含alpha信息,然后只需添加

GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND); 
在绘制纹理之前

。完成后请务必禁用GL_BLEND。

答案 1 :(得分:3)

您很可能希望使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)作为混合函数,并且必须确保还将纹理的alpha值写入gl_FragColor片段着色器输出变量。

对于所有工作,您上传的纹理数据必须包含alpha值,并且您必须使用支持Alpha通道(RGBA,RGBA8等)的纹理格式。

您可以通过简单地将Alpha值路由到RGB颜色组件并检查您获得的图像来验证这一点。

修改

在您的图片加载代码中,您忘记复制Alpha通道!尝试 davebytes 给出的建议。

答案 2 :(得分:1)

您的初始着色器很好。 alpha是颜色操作中固有的,根据混合/模式/等,它可能无法应用。

如果你做了fragcolor = base.aaa你的纹理会变黑,这意味着你的纹理数据是'坏'。

看着你的纹理负荷,是的,这是错的。你永远不会复制alpha,只有rgb。假设java将字节数组清除为0,则所有alpha都将为零,这将使您获得黑盒子,这会导致图像在启用Alpha混合时“消失”。

为了简化您的生活,您可以简单地正常加载位图并使用GLUtils帮助程序上传而不是直接使用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);

这样的事情。然后启用混合,如果没有预乘,则使用src + invsrc混合模式,然后渲染,你应该得到所需的结果。