2011-08-17 176 views
8

我刚学习使用OpenGL ES 2.0 for Android。我一直试图简单地在屏幕中间显示纹理,这很容易,但我似乎无法使PNG alpha正常工作。图像将以黑色背景显示,或根据我使用的设置将整个图像稍微混合为背景颜色。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,并且我刚刚设置了错误的东西。

编辑::这里是 “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的世界看起来),所以如果你认为别的原因我一定会为你发布。对不起,所有的烦恼!

+0

确保在将图像加载到纹理中(而不是加载RGB图像或创建RGB纹理)时保留来自PNG的Alpha通道。当然,确保你调用'glEnable(GL_BLEND)'(除了设置正确的'glBlendFunc')。但有些代码肯定会帮助你多一点。也许只是图像加载和纹理创建代码以及您设置混合状态的代码。当然,因为你是新来者,不要忘记告诉你自己有关接受和投票功能。 –

+0

片段着色器看起来是有效的,因为它正确地将纹理的alpha代理给了片段颜色。所以你不会发布一些你的加载和纹理创建代码。 –

+0

您可以尝试禁用混合的'gl_FragColor = vec4(baseColor.aaa,1.0)'。这应该会给你一个alpha通道的灰度图像。 – trenki

回答

3

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

对于这一切工作您上传的纹理数据必须包含一个alpha值,你必须使用支持alpha通道的纹理格式(RGBA,RGBA8等)。

您可以通过简单地将alpha值路由到RGB颜色组件并检查获取的图像来验证此情况。

编辑:

在你的图像加载代码,你忘了alpha通道复制!试试davebytes给出的建议。

+0

如果我在glBlendFunc上使用该设置,图像根本不会显示。看起来,glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA)对我来说最适合,或者至少最接近正常工作。 我还应该提到,当图像混合到背景中时,它似乎也删除了图像的透明部分。 我相信我的问题在于片段着色器,因为我还不太了解它,它主要是来自没有使用透明度的示例的代码副本。我会在主帖中发布。 – DaeKo

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混合模式,并渲染,您应该得到所需的结果。

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

包括阿尔法信息,则只需添加

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

绘制纹理权之前。请记住在完成后禁用GL_BLEND。