Search code examples
javaandroidopengl-esfbo

GLES: FrameBuffer object showing weird images


I'm currently trying to get FBOs working in my game using Android Studio.

I've already managed to set up an FBO and to draw a filled white rectangle (64x64px) to it.

But when I'm trying to render the FBO's color buffer onto the screen, random noise and weird images appear.

Snapshot of the weird images which the FBO is producing
Snapshot of the weird images which the FBO is producing



I neither know why my solid colored rectangle isn't showing up nor why there is a profile picture of a Whatsapp chat, which isn't intended to be so.

This is how I initialize my FrameBuffer object:

private void initFrameBuffer() {

    int[] temp = new int[1];

    //Create a texture object and bind it. This will be the color buffer.
    glGenTextures(1, temp, 0);
    colorTexture = temp[0];

    glBindTexture(GL_TEXTURE_2D, colorTexture);

    //Set parameters. We're probably using non-power-of-two (NPOT) dimensions,
    //so some values may not be available for use!
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    //Create a texture storage:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);

    //Create framebuffer object and bind it.
    glGenBuffers(1, temp, 0);
    frameBufferID = temp[0];
    glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID);

    //Create a depth buffer and bind it.
    glGenRenderbuffers(1, temp, 0);
    depthBuffer = temp[0];

    glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);

    //Allocate storage for the depth buffer.
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);

    //Attach the depth buffer and the texture (color buffer) to the framebuffer object.
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D, colorTexture, 0);

    //Check if GL ES is happy with all this ... :)
    int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        throw new RuntimeException("Framebuffer not complete! StatusID: " + status);
    }

    //Switch back to the default framebuffer.
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

How I'm drawing the whole scenery:

@Override
public void draw() {
    GLES20.glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject.getID());
        Graphics.setColor(new Color(255, 255, 255));
        Graphics.fillRect(0, 0, 64, 64);
    GLES20.glBindFramebuffer(GL_FRAMEBUFFER, 0);

    Graphics.drawFrameBuffer(frameBufferObject, 0, 0, frameBufferObject.getWidth(), frameBufferObject.getHeight());
}

Last but not least, this is how I draw the FBO's color buffer to the screen using a simple shader program:

public static void drawFrameBuffer(FrameBufferObject frameBufferObject, int x, int y, int w, int h) {

    ImageMesh mesh = new ImageMesh(generateMesh(x, y, w, h));

    imageShader.start();

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameBufferObject.getColorTexture());
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);

    //Enable generic vertex attribute array
    GLES20.glEnableVertexAttribArray(imageShader.positionHandle);

    //Prepare triangle coordinate data
    GLES20.glVertexAttribPointer(imageShader.positionHandle, 3, GLES20.GL_FLOAT, false, 0, mesh.baseMesh.vertexBuffer);

    //Enable generic vertex attribute array
    GLES20.glEnableVertexAttribArray(imageShader.texCoordLocation);

    //Prepare the textureCoordinates
    GLES20.glVertexAttribPointer(imageShader.texCoordLocation, 2, GLES20.GL_FLOAT, false, 0, mesh.uvBuffer);

    //Apply the projection and view transformation
    GLES20.glUniformMatrix4fv(imageShader.mtrxHandle, 1, false, Renderer.getUMVPMatrix(), 0);

    //Set the sampler texture unit to 0
    GLES20.glUniform1i(imageShader.samplerLoc, 0);

    //Draw the triangles
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, mesh.baseMesh.indices.length, GLES20.GL_UNSIGNED_SHORT, mesh.baseMesh.drawListBuffer);

    //Disable vertex arrays
    GLES20.glDisableVertexAttribArray(imageShader.positionHandle);
    GLES20.glDisableVertexAttribArray(imageShader.texCoordLocation);

    //Unbind the texture
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

    imageShader.stop();
}

The Android API-Level I'm using is 22. Also, the FBO I've created fits the screen size exactly, which is 720x1280px.

This problem wasted a whole day of mine, and I'm have no idea what could be causing this problem!

I'm really grateful for any kind of help! <3


Solution

  • After a few hours I finally managed to get it working though!

    The main reason for the error was, that I haven't set up my FBO correctly, which resulted in this glitchy image.

    So I watched an OpenGL tutorial by ThinMatrix tutorial on Youtube, and he was able to get me out of this bad situation. :)

    Here's my updated FrameBuffer Object code:

    public class FrameBufferObject {
    
    private int width;
    private int height;
    
    private int frameBufferID;
    private int texture;
    private int depthTexture;
    private int depthBuffer;
    
    private Matrices.MatrixKit previousMatrixKit;
    
    public FrameBufferObject(int width, int height) {
        this.width = width;
        this.height = height;
    
        initFrameBuffer();
    }
    
    private void initFrameBuffer() {
    
        frameBufferID = createFrameBuffer();
        texture = createTextureAttachment();
        depthTexture = createDepthTextureAttachment(width, height);
        depthBuffer = createDepthBufferAttachment();
    
        int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (status != GL_FRAMEBUFFER_COMPLETE) {
            Logger.error("Framebuffer not Complete! STATUS: " + status + "\nWidth: " + width + "\nHeight: " + height);
        }
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }
    
    private int createFrameBuffer() {
        int[] ids = new int[1];
        glGenFramebuffers(1, ids, 0);
        int frameBufferID = ids[0];
        glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID);
    
        int[] buffers = new int[] {GL_COLOR_ATTACHMENT0};
    
        GLES30.glDrawBuffers(1, buffers, 0);
        return frameBufferID;
    }
    
    private int createTextureAttachment() {
        int[] ids = new int[1];
        glGenTextures(1, ids, 0);
        int colorTexture = ids[0];
    
        glBindTexture(GL_TEXTURE_2D, colorTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
        return colorTexture;
    }
    
    private int createDepthTextureAttachment(int width, int height) {
        int[] ids = new int[1];
        glGenTextures(1, ids, 0);
        int depthTextureID = ids[0];
    
        glBindTexture(GL_TEXTURE_2D, depthTextureID);
        //glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTextureID, 0);
        return depthTextureID;
    }
    
    private int createDepthBufferAttachment() {
        int[] ids = new int[1];
        glGenBuffers(1, ids, 0);
        int depthBuffer = ids[0];
    
        glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
        return depthBuffer;
    }
    
    public int getColorTexture() {
        return this.texture;
    }
    
    public void bindFrameBuffer() {
        glBindTexture(GL_TEXTURE_2D, 0); //just make sure the texture isn't bound
        glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID);
        glViewport(0, 0, width, height);
    
        previousMatrixKit = Renderer.getMatrices();
        Renderer.setMatrixKit(Matrices.createMatrixKit(width, height));
    }
    
    public void unbindFrameBuffer() {
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glViewport(0, 0, previousMatrixKit.width, previousMatrixKit.height);
    
        Renderer.setMatrixKit(previousMatrixKit);
    }
    
    public int getWidth() {
        return this.width;
    }
    
    public int getHeight() {
        return this.height;
    }
    
    public int getID() {
        return this.frameBufferID;
    }
    }