3
votes

*I've been trying my very best to implement renderable texture functionality using OpenGL's framebuffering together with the LWJGL library from Java. However, the result that I always get is a 100% **black ** texture.*

I'm simply asking for some advice of what the problem is. I'm not rendering any specific shapes. I bind my generated framebuffer and call a glClearColor(1, 0, 0, 1); and then a glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); and then unbind the framebuffer. But when I try to render the texture bound to the framebuffer, the texture only shows black, where it actually should be red, right?

Also, the glCheckFramebufferStatus() returns GL_FRAMEBUFFER_COMPLETE so I suppose that the error lies within the rendering part, rather than the initialization phase. But I'll show the initialization code anyways.

The initialization code:

public RenderableTexture initialize(int width, int height, int internalFormat, int[] attachments, boolean useDepthBuffer) {
    if(!GLContext.getCapabilities().GL_EXT_framebuffer_object) {
        System.err.println("FrameBuffers not supported on your graphics card!");
    }

    this.width = width;
    this.height = height;
    hasDepthBuffer = useDepthBuffer;

    fbo = glGenFramebuffers();
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);

    id = glGenTextures();
    glBindTexture(GL_TEXTURE_2D, id);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer) null);


    if(useDepthBuffer) {
        rbo = glGenRenderbuffers();
        glBindRenderbuffer(GL_RENDERBUFFER, rbo);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
    }

    glFramebufferTexture2D(GL_FRAMEBUFFER, attachments[0], GL_TEXTURE_2D, id, 0);

    int[] drawBuffers = new int[attachments.length];

    for(int i = 0; i < attachments.length; i++)
        if(attachments[i] == GL_DEPTH_ATTACHMENT)
            drawBuffers[i] = GL_NONE;
        else
            drawBuffers[i] = attachments[i];

    glDrawBuffers(Util.toIntBuffer(drawBuffers));

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        System.err.println("Warning! Incomplete Framebuffer");
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    return this;
}

internalFormat has the value of GL_RGBA8 and width and height have the value of 512 and 512. attachments[] only contains 1 value and that's GL_COLOR_ATTACHMENT0. useDepthBuffer is set to true.

The code above is only called once.

This is the rendering code:

public RenderManager draw() {
    glClearColor(bg.x, bg.y, bg.z, bg.w);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    texture.bindAsRenderTarget(true);
    texture.releaseRenderTarget();
    quad.draw();

    return this;
}

I set the clear color to black (0, 0, 0, 1) and then clear the screen. I then call texture.bindAsRenderTarget(true);. The texture object is the one who contains the initialize method from above so some variables are shared between that method and bindAsRenderTarget().

This method looks like this:

public RenderableTexture bindAsRenderTarget(boolean clear) {
    glViewport(0, 0, width, height);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
    glClearColor(1, 0, 0, 1f);
    if(clear)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    return this;
}

As you can see I adjust the viewport to the size of the texture / framebuffer. I then bind the framebuffer and set the clear color to red. Then, since I passed true in the rendering code, it (as i believe) clears the currently bound framebuffer to red.

texture.releaseRenderTarget(); adjusts the viewport to fit the display and then calls glBindFramebuffer(GL_FRAMEBUFFER, 0);

The final line of code quad.draw(); simply binds the textureID of the texture bound to the framebuffer and then draws a simple quad with it.

That's pretty much all there is.

I can assure you that I'm rendering the quad correctly, since I can bind textures loaded from PNG files to it and the texture is successfully shown.

So to make things clear, the main question is pretty much:

Why on earth is the texture black after the clear as it should be red? Where and what am I doing wrong?

EDIT: I have a feeling that it might have to do with something about the bounding of different gl ojects. Does the renderbuffer have to be bound at the point of rendering to it's framebuffer? Does it not? Does it matter? How about the texture? at what points should they be?

1
When you've finished drawing into the framebuffer texture and unbound the framebuffer, just before you want to render to the back buffer again, try glDrawBuffer(GL_BACK);Robinson
Thanks for the answer Robinson :) Unfortunately, this did not solve the answer. The problem probably lies within the fbo rendering and not the back buffer rendering.SporreKing
Well, it is unclear from your code if the issue is actually rendering into the texture/FBO, or if it is rendering the texture to the default framebuffer.derhass
The point of the post is that I don't really know where the problem is, but I suppose that it lies within rendering to the texture rather than rendering to the defaultbframebuffer. However, I added a main question to the post :)SporreKing
Are you creating a Core Profile context?Reto Koradi

1 Answers

1
votes

I did something very stupid. The class that I initialized the fbo texture within (RenderableTextue.class) was a subclass of Texture.class. The binding method including the textureID was supposed to be inherited from the Texture class as I had declared the id variable as protected. However, I had accidently created a local private variable within the subclass, and thus, when generating the texture, saving the textureID to the local id variable and when binding, using the uninitialized id from the superclass. Sorry for anyone trying to solve this without being able to do so :s