Android OpenGL 透明纹理绘制出黑色

13

我正在尝试在我的应用程序中绘制透明纹理(文本)。奇怪的是,它在最新的Nexus 7和第二代Moto X上可以工作,但在原始Nexus 7上纹理就只是黑色的。我已经启用了混合,并且纹理的大小为512x512,因此不是2的幂问题。我也只是使用GL10,这应该在所有设备上都受支持,对吗?有什么原因纹理在仅此设备上无法正常工作吗?

gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnable(GL10.GL_TEXTURE_2D);
// text drawn here
gl.glDisable(GL10.GL_TEXTURE_2D);
gl.glDisable(GL10.GL_BLEND);

这里是纹理初始化,我在这里加载纹理集:

public void loadGlyphs(GL10 gl, Context context) {
    Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.text_bitmap);
    gl.glGenTextures(1, textures, 0);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

    bitmap.recycle();
}

顶部显示的是旧的Nexus 7上发生的事情。底部的图片是Moto X。

Bad

Good

编辑:这里是一个完整的例子。没有透明度,在旧版Nexus 7上不会绘制任何内容。如果我取消纹理的设置,它会像应该一样以白色绘制正方形。

MainActivity.java

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;


public class MainActivity extends Activity {

    private GLSurfaceView glView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        glView = new TestView(this);
        setContentView(glView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        glView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        glView.onResume();
    }
}

TestView.java

import android.content.Context;
import android.opengl.GLSurfaceView;

public class TestView extends GLSurfaceView {
    private TestRenderer renderer;

    public TestView(Context context) {
        super(context);

        renderer = new TestRenderer(context);
        setRenderer(renderer);
    }
}

TestRenderer.java

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class TestRenderer implements GLSurfaceView.Renderer {

    private FloatBuffer floatBuffer;
    private FloatBuffer textureBuffer;
    private Context context;
    private int[] textures;

    public TestRenderer(Context context) {
        this.context = context;
        textures = new int[1];
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        floatBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        floatBuffer.put(-0.5f);
        floatBuffer.put(-0.5f);
        floatBuffer.put(-0.5f);
        floatBuffer.put(0.5f);
        floatBuffer.put(0.5f);
        floatBuffer.put(-0.5f);
        floatBuffer.put(0.5f);
        floatBuffer.put(0.5f);
        floatBuffer.rewind();

        textureBuffer = ByteBuffer.allocateDirect(4*2*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        textureBuffer.put(0);
        textureBuffer.put(1);
        textureBuffer.put(0);
        textureBuffer.put(0);
        textureBuffer.put(1);
        textureBuffer.put(1);
        textureBuffer.put(1);
        textureBuffer.put(0);
        textureBuffer.rewind();

        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.test);
        gl.glGenTextures(1, textures, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        GLU.gluPerspective(gl, 45.0f, (float) w / (float) h, 0.1f, 100.0f);
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    public void onDrawFrame(GL10 gl) {
        gl.glLoadIdentity();
        gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        gl.glTranslatef(0.0f, 0.0f, -5.0f);

        gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glFrontFace(GL10.GL_CW);
        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, floatBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisable(GL10.GL_TEXTURE_2D);
    }
}

编辑:我在网上找到了一个例子。如果我禁用GL_TEXTURE_2D,我会得到一个白色的正方形。如果我启用GL_TEXTURE_2D,我什么也看不到。

MainActivity public class MainActivity extends Activity {

    private GLSurfaceView glSurfaceView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        glSurfaceView = new GLSurfaceView(this);

        glSurfaceView.setRenderer(new GlRenderer(this));
        setContentView(glSurfaceView);
    }

    @Override
    protected void onResume() {
        super.onResume();
        glSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        glSurfaceView.onPause();
    }

}

GlRenderer

public class GlRenderer implements Renderer {

    private Square square;
    private Context context;

    public GlRenderer(Context context) {
        this.context = context;
        this.square = new Square();
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        gl.glTranslatef(0.0f, 0.0f, -5.0f);
        square.draw(gl);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        if(height == 0) {
            height = 1;
        }
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();

        GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);

        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        square.loadGLTexture(gl, this.context);

        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glShadeModel(GL10.GL_SMOOTH);
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
        gl.glClearDepthf(1.0f);
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glDepthFunc(GL10.GL_LEQUAL);
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
    }
}

正方形

public class Square {

    private FloatBuffer vertexBuffer;
    private float vertices[] = {
            -1.0f, -1.0f,  0.0f,
            -1.0f,  1.0f,  0.0f,
            1.0f, -1.0f,  0.0f,
            1.0f,  1.0f,  0.0f
    };

    private FloatBuffer textureBuffer;
    private float texture[] = {
            0.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 1.0f,
            1.0f, 0.0f
    };

    private int[] textures = new int[1];

    public Square() {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        vertexBuffer = byteBuffer.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);
        byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        textureBuffer = byteBuffer.asFloatBuffer();
        textureBuffer.put(texture);
        textureBuffer.position(0);
    }

    public void loadGLTexture(GL10 gl, Context context) {
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.test);

        gl.glGenTextures(1, textures, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

        bitmap.recycle();
    }

    public void draw(GL10 gl) {
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glFrontFace(GL10.GL_CW);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    }
}

@MuertoExcobito 添加了那段代码。 - David M
我正在尝试找出您正在使用哪些纹理格式,但 GLUtils.texImage2d 内部已经做了这个工作,所以它并没有提供太多信息。但是,也许您的绘制代码依赖于它处于特定通道(例如 alpha),而它却被放在了红色通道中?当切换到 3.2 核心版本时,这是 OpenGL 中的一个问题,因为 GL_ALPHA 已被弃用。 - MuertoExcobito
文本图集只是带有透明背景的白色文本(png)。 - David M
尝试添加GLES20.glGenerateMipMap(GLES20.GL_TEXTURE_2D); - samgak
@samgak 看起来似乎没有任何作用。 仍然是空白黑色纹理。 - David M
显示剩余5条评论
2个回答

2
你的纹理使用的是哪种颜色格式? 它是否与着色器期望的颜色格式相匹配?
如果你的颜色格式是RGBA8888,而着色器期望的是RGB256,那么就会出现问题。(它将在错误的位置寻找alpha通道信息)

我刚刚记录了GLUtils.getInternalFormat(bitmap)并得到了6408,这对应于GL_RGBA。 - David M
嗯,你可以尝试在GIMP中打开你的纹理并以RGB模式导出它。然后尝试以灰度模式导出并查看差异?我建议你尝试以下几点:将着色器精度更改为HIGHP float。尝试将alpha通道从第一个位平面切换到最后一个位平面(或反之亦然)。你是否使用了纹理压缩?某些压缩算法不支持alpha通道... - DKIT
在RGB和灰度之间切换并没有对其产生影响。我在GIMP中将png压缩级别从9改为0,但没有任何区别。我还添加了另一个示例。有趣的是,该示例中没有透明度,但图像仍未显示。它可能不喜欢GLUtils生成它吗?纹理在其他应用程序中可以工作,所以我真的不确定问题出在哪里。 - David M
我不是指GIMP中的压缩,我是指当您将纹理上传到OpenGL时使用纹理压缩 --- 在此处阅读更多信息:https://www.opengl.org/wiki/Image_Format - DKIT
我在应用程序中没有使用任何纹理压缩。这似乎是旧设备上的问题。我刚刚在摩托罗拉Bionic(XT875)上尝试了一下,遇到了同样的问题。如果我启用GL_TEXTURE_2D,我什么也看不到,如果我不启用它,我会得到白色的四边形。看起来我正在使用的任何函数都不应该引起问题,而且我只使用GLES 1.0,因此任何设备都应该支持。你觉得有哪些方法有问题吗? - David M

1
经过大量的故障排除,我成功地解决了两个设备(以及可能所有设备)上的问题,方法是添加纹理包装,如下所示:
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

我不确定为什么这对两个设备是必要的,但对另外两个设备却不是。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接