2
votes

This is my first venture into using framebuffers instead of using the default one (and then drawing the framebuffer texture onto the default framebuffer). I'm doing something wrong along the way and I'm not sure what. I'll list every step that I'm doing since this example is pretty close to an MCVE.

All the glEnable() states I want are set correctly, and this isn't a cull face issue according to RenderDoc. I've registered a callback with glDebugMessageCallback and I get no errors. Also I attach a depth/stencil texture but do not use it in this example (but will be using it when I fix this issue).

My code is essentially as follows (everything is sequential so if there's a binding at step X then it is also bound for step X + 1):

0) Create VBO/VAO/shader program for drawing the FBO texture

// This is fine according to RenderDoc

1) Create the framebuffer with the texture we'll render to and a depth/stencil texture:

glGenFramebuffers(1, &fbo);

// Indenting added to this MCVE only
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

    [same as above but now with a depth24/stencil8 texture]

    assert GL_FRAMEBUFFER_COMPLETE (which returns true)
glBindFramebuffer(GL_FRAMEBUFFER, 0);

2) Rendering phase, clear default framebuffer

glClearColor(0.1, 0.1, 0.1, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glViewport(0, 0, w, h)

3) Bind the new FBO and clear it before we draw a quad

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(color/stencil/depth bits)
glViewport(0, 0, w, h)

4) Draw a quad to the new framebuffer by 2 triangles

// Not needed in the MCVE but will be required in the actual
// application, so I'll put it in here in the odd case this
// is somehow important
glClearDepth(1.0);

// Do I need to bind the depth/stencil texture here as well?
glBindTexture(GL_TEXTURE_2D, texture);
    bind shader program
        set mvp uniform
        bind vbo
            upload 2 triangles to vbo
            // My goal is to draw the quad on the second
            // framebuffer here
            glDrawArrays(...)
        unbind vbo
    unbind shader
glBindTexture(GL_TEXTURE_2D, 0);

5) Draw the FB texture onto the default framebuffer

glBindFramebuffer(GL_FRAMEBUFFER, 0);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
    // QUESTION: Do I also need to bind the depth/stencil here
    //           if I was planning to use it? Right now I do not
    //           and I'm wondering if that's a mistake
    bind second shader program
        create vbo/vao
        bind vbo
            fill up vbo with a quad to draw the framebuffer texture
            glDrawArrays(...)
        unbind vbo
    unbind second shader program
glBindTexture(GL_TEXTURE_2D, 0);

There are no uniforms being set in the last step because I'm giving it NDC coordinates and don't need a transformation.

When I run the program and I bring up the framebuffer, I can tell it's doing something because my FPS drops from 4000-5000 fps down to about 2000 fps and I start getting coil whine, and when I switch of framebuffer rendering then it goes back up to 4k-5k fps and the coil whine disappears.

However nothing draws...

I opened it up in RenderDoc to see what is up, and this is what it's telling me:

1) All my data (VBOs, viewport, clear colors, etc) are exactly as I want it. If there's any mistake in the data you will see then it's a misunderstanding of OpenGL on my part and not some code error.

2) Both the second framebuffer and default framebuffer mesh results seem to be outside the frustum, have I done something wrong?

My first step is to draw the stuff onto the second framebuffer (I'm rendering a quad on an 800x600 viewport where I want to fill the top half (so 0,0 to 800,300, where the ortho matrix is designed to have the top left be the origin)), and then later on I'll draw the texture for this 'second framebuffer' onto it. So for this second framebuffer... this is what I see:

enter image description here

enter image description here

There is in fact a gap between the white box (which I assume is the view frustum) which has me wondering if I'm drawing the two counter-clockwise triangles at the wrong Z. Renderdoc says I'm drawing them at NDC Z coordinates of -0.60, so I thought that's in range since the NDC cube is from -1 to +1 on all 3 axes.

The quad itself is in line with it as seen in the second image, but part of me wonders if I've somehow done something wrong with my orthographic matrix... however if an NDC Z value of -0.60 is okay then I assume the transformation is doing what I want it to.

RenderDoc says that the quad is being rendered according to the framebuffer texture, so that is good. To debug it, I did:

// Force any draw we have to be red and max alpha to debug it
fragColor.x = 1.0;
fragColor.w = 1.0;

and we get this as the output of the second framebuffer according to RenderDoc:

enter image description here

so that's good! I appear to be drawing the quad on the second framebuffer correctly.

Then my problem must be that I'm not rendering correctly to the default framebuffer, but I don't know why.

To try and debug this, I did the same thing for the shader with the default framebuffer (except I'll do green instead of red in the fragment shader). However no green was drawn, yet RenderDoc says I did everything else right. Here is the information:

Vertex shader:

    #version 440 core

    layout(location = 0) in vec3 pos;
    layout(location = 1) in vec2 uv;

    out vec2 uvFrag;

    void main() {
        uvFrag = uv;
        gl_Position = vec4(pos, 1.0);
    }

Fragment shader:

    #version 440

    in vec2 uvFrag;

    out vec4 fragColor;

    uniform sampler2D framebufferTexture;

    void main() {
        fragColor = texture(framebufferTexture, uvFrag);

        // Debug whether its actually even drawing the quad
        fragColor.y = 1.0;
        fragColor.w = 1.0;
    }

Mesh output:

enter image description here

I'm not sure where I'm screwing up. Are NDC Z coordinates of -0.90 okay? If they are, then the two triangles that make up the quad are getting there... but they aren't being colored. Because of the debugging with green/alpha at 1.0, even if the texture is somehow wrong then I should be getting at least something being drawn.

The pipeline state is fine, swapping the buffers works for other things so I am pretty sure I'm not doing anything wrong there. No GL errors are found.

Any guesses on why the triangles are not getting picked up by the default framebuffer? I'm only getting the clear color on the default framebuffer, which makes me wonder if I'm binding things at the wrong place or I've screwed up rebinding the default framebuffer, or if the NDC coordinates in the last image I posted are wrong, or if it's something else I don't know.

2
Can you provide the renderdoc capture file? Also, "example is pretty close to an MCVE" - a proper MCVE would help a lot, it would allow us to try to debug your code. - HolyBlackCat
@HolyBlackCat I uploaded a renderdoc capture at the end of my post. Sadly I can't post the full code because it's part of a large code base. In retrospect I should have just done a fully standalone MCVE. If you (or anyone else) cannot find what is wrong, I will repost a new question with a standalone MCVE. It will likely be extremely similar to what you see in this post and the stuff in the renderdoc file. - Water
@Water Is the depth test disabled when the 2nd pass is rendered? - Rabbid76
@Rabbid76 It's enabled for the second pass, or rather I don't disable the depth test ever. The only time I play with the depth is when its reset in glClear() and one time where I set it to be equal to 1.0f for when the second framebuffer is rendered. Should I try disabling it, or is it something I shouldn't have on? I can disable it and try because I'm only rendering one framebuffer (and I know the order anyways do disabling depth should not affect my final result, so I could try it if you want me to). - Water
@Water You don't clear the depth buffer of the default framebuffer, so you've to disable the depth test. - Rabbid76

2 Answers

1
votes
// Do I need to bind the depth/stencil texture here as well?
glBindTexture(GL_TEXTURE_2D, texture);

Actually you shouldn't bind there anything at all. If you bind the texture to a texture unit, you potentially create a feedback loop (if the texture unit is sampled from in the shaders). It's allowed to leave a render target texture bound, if the pipeline configuration won't read from it, but in case of "won't render" it's best to make sure, that render target textures are not in a "readable" state when drawing to.

1
votes

Before the 2nd pass is rendered, either the depth test has to be disabled

glDisable(GL_DEPTH_TEST);

or the depth buffer of the render target (default framebuffer) has to be cleared:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Note, by default the depth function is GL_LESS, so the depth test will fail, if the depth buffer is not "cleared", by 1.0. Even if depth test succeeds in the 1st frame, in the 2nd frame the color buffer is cleared, the depth test will fail and nothing is rendered at all.