我正在学习使用适用于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的世界中),所以如果你认为还有其他原因我也一定会为你发布。抱歉所有的麻烦!
答案 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混合模式,然后渲染,你应该得到所需的结果。