OpenGL ES 2.0 PNG alpha通道

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

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

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

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

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做任何事情,它来自一个完全不使用它的例子,但我仍然不太了解片段着色器,因为当它将图像融合到图像中时似乎“有点”工作背景,我认为它是以某种forms使用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 > 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的世界中),所以如果你认为别的东西是原因我也一定会为你发布。 对不起,所有的烦恼!

更改

  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。

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

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

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

编辑:

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

你的初始着色器很好。 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混合模式,如果没有预乘,并渲染,你应该得到所需的结果。