Android OpenGL Transparent Texture Draws Black
Asked Answered
U

2

13

I'm trying to draw transparent textures (text) in my app. Strangely, it works on the newest Nexus 7 and on my second generation Moto X but on the original Nexus 7 the textures are just black. I've got blending enabled and the texture is 512x512 so it's not a power-of-two issue. I'm also just using GL10 which should be supported on everything, right? Any reasons the textures wouldn't work on just this device?

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

And here's the texture initialization, where I load the texture atlas:

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

Top shows what happens on the old Nexus 7. The bottom picture is a Moto X.

Bad

Good

Edit: Here's a complete example. No transparency, doesn't draw anything on the old Nexus 7. If I get rid of the texture stuff it draws the square in white like it should.

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

Edit: Here's an example I found online. If I disable GL_TEXTURE_2D I get a white square. If I enable GL_TEXTURE_2D I get nothing.

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

Square

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);
    }
}
Undressed answered 16/6, 2015 at 13:47 Comment(10)
Perhaps it's the type of texture you're using. Show your texture creation/initialization code.Hemp
@Hemp added that codeUndressed
I was driving at finding out which texture formats you're using, but GLUtils.texImage2d does that internally, so it's not that informative. But, perhaps your drawing code is depending it on being in a specific channel (eg. alpha), and it's putting it in red instead? This was a problem in OpenGL when switching to 3.2 core, because GL_ALPHA was deprecated.Hemp
The text atlas is just white text with a transparent background (pngUndressed
Try adding GLES20.glGenerateMipMap(GLES20.GL_TEXTURE_2D);Todtoday
@Todtoday that doesn't seem to have done anything. Still blank black texturesUndressed
This is way too little information, much can be wrong. Can you try to pinpoint the actual issue. For instance are you sure the textures are even loaded on the GPU? Maybe try a non transparent textures and disable the blend to see if that actually works. Also are you using multiple threads? And maybe force a specific active texture if not done already.Stockpile
@MaticOblak added a full example. Even without transparency nothing is drawn. If I disable texturing I get a square like I should. Again, the example works perfectly on the new Nexus 7 and Moto X but fails on the old Nexus 7Undressed
Then it would seem your textures simply are not loaded or not drawn or the texture data is actually not uploaded and is black . It has nothing to do with the transparency or the blending as it seems. Unfortunately even if it works on other devices it might be your bug, not the device's. It can simply work on others after you have forgotten to set a simple value or something... A needle in a haystack.Stockpile
sorry, maybe this doesn't answer to your question but, why are you drawing text using a bitmap preconfigured? Why you aren't creating the texture from text direclty? Like drawing text on canvas and converting to texture? I think png's in the drawable directory can mess a bit the renderingPhyllida
U
1

After much troubleshooting, I was able to solve the problem on both devices (and presumably all devices) by adding texture wrapping as so:

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

I'm not sure why this was necessary for two devices but not the other two, though.

Undressed answered 30/6, 2015 at 13:3 Comment(0)
A
2

What color format is it your texture is using? Does it match the color format your shader is expecting?

If your color format is RGBA8888 and the shader expects RGB256 you can get problems like this. (It will look for alpha channel information at the wrong place)

Adley answered 24/6, 2015 at 12:48 Comment(5)
I just logged GLUtils.getInternalFormat(bitmap) and got back 6408, which corresponds to GL_RGBAUndressed
Hmmm, can you try opening your texture in GIMP for example and export it in RGB mode? Then try to export in grayscale and see the difference? Some other things I would try: Try to change the precision of your shader to HIGHP float. Try to switch alpha channel from the first bitplane to the last bitplane (or vice versa). Are you using texture compression? Some compression algorhitms does not support alpha channel...Adley
Switching between RGB and Grayscale hasn't affected it at all. I changed png compression level in GIMP from 9 to 0 with no difference. I also added another example. Interestingly, there's no transparency in that example and the image is still not displayed. Could it just not like GLUtils generating it? Textures work in other apps, so I'm really not sure what the deal isUndressed
I did not mean the compression in GIMP, I meant if you use texture compression when you upload your texture to OpenGL --- Read about it here: opengl.org/wiki/Image_FormatAdley
I'm not using any texture compression in the app. It appears to be an issue on older hardware. I just tried it on a Motorola Bionic (XT875) and I'm having the same problem. If I enable GL_TEXTURE_2D I get nothing and if I don't enable I get the white quads. It doesn't look like any of the functions I'm using should cause problems and I'm only using GLES 1.0 so that should be available on any device. Any methods look problematic to you?Undressed
U
1

After much troubleshooting, I was able to solve the problem on both devices (and presumably all devices) by adding texture wrapping as so:

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

I'm not sure why this was necessary for two devices but not the other two, though.

Undressed answered 30/6, 2015 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.