How to capture an image drawn on a different texture attachment?

I am not certain what I am doing incorrectly.

Let me first explain what I am trying to accomplish. I just trying to view a scene as normal and whenever I click on a key capture the distance from the camera to each pixel in the scene. In addition, I want the distance to each pixel to be stored in a texture attached to my framebuffer (but not as a color_attachment0 but rather as a second color_attachment1).

I get an error whenever I run the instruction glReadPixelsf(0,0, SCR_WIDTH, SCR_HEIGHT, GL_RED, GL_FLOAT) (SCR_WIDTH = 800 and SCR_HEIGHT= 600)

Am I specifying correctly the output of my fragment shader?

Here are the main parts involved.

Vertex shader of ShaderDepth

#version 440 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec4 pos;
void main()
{
    pos = view * model * vec4(aPos, 1.0f);   
    gl_Position = projection * view * model * vec4(aPos, 1.0f);
}

fragment shader of ShaderDepth

#version 440 core
layout(location = 1) out float depth; // second texture COLOR_ATTACHMENT1
in vec4 pos;
//out float depth;
void main()
{
    depth = length(pos.xyz);
}

Usually, I would have just have just specified depth as simple float as shown commented on the fragment shader. My understanding is that because I am using COLOR_ATTACHMENT1 rather than 0 I need to use layout() instead??? Is this correct?

My main render loop:

 while (not glfwWindowShouldClose(window)):

        # captures keys
        processInput(window)

        # render
        glClearColor(0.1, 0.1, 0.1, 1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glEnable(GL_DEPTH_TEST)

        # define basic viewing parameters
        view = camera.GetViewMatrix()
        projection = glm.perspective(glm.radians(camera.Zoom), SCR_WIDTH / SCR_HEIGHT, 0.1, 100.0)     

        # default shader
        shader = shaderDisplay

        if camera.capture:
            # bind the framebuffer
            glBindFramebuffer(GL_FRAMEBUFFER, fbo)

            glClearColor(0.1, 0.1, 0.1, 1.0)
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )

            # set shader to shader depth
            shader = shaderDepth
            
        # set uniforms on shader
        shader.use()
        shader.setMat4("view", view)
        shader.setMat4("projection", projection)
        
        # draw floor
        glBindVertexArray(planeVAO)
        glBindTexture(GL_TEXTURE_2D, floorTexture)
        shader.setMat4("model", glm.mat4(1.0))
        glDrawArrays(GL_TRIANGLES, 0, 6)
        glBindVertexArray(0)
        
        # cube 1
        glBindVertexArray(cubeVAO)
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, cubeTexture)
        model = glm.mat4(1.0)
        model = glm.translate(model, glm.vec3(0.0, 0.0, -0.75))
        shader.setMat4("model", model)
        glDrawArrays(GL_TRIANGLES, 0, 36)
        
        # cube 2
        scale = 0.35
        model = glm.mat4(1.0)
        model = glm.scale(model, glm.vec3(scale, scale, scale))
        model = glm.translate(model, glm.vec3(0.0, 0, 2.0))
        shader.setMat4("model", model)
        glDrawArrays(GL_TRIANGLES, 0, 36)
        
        glBindVertexArray(0)      

        glfwSwapBuffers(window)
        glfwPollEvents()

        # end of capture
        if camera.capture:
                   
            # save outputs   ERROR HERE         
            depth = glReadPixelsf(0,0, SCR_WIDTH, SCR_HEIGHT, GL_RED, GL_FLOAT)
            np.save('depth', np.flipud(depth))

            # switch shader back to display shader
            shader = shaderDisplay

            # reset capture flag
            camera.capture = False

            # unbind framebuffer back to default
            glBindFramebuffer(GL_FRAMEBUFFER,0)

Finally my framebuffer is as such,

    # create depth buffer
    fbo= glGenFramebuffers(1)
    glBindFramebuffer(GL_FRAMEBUFFER, fbo)

    # COLOR DEPTH 
    color_depth_tex = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, color_depth_tex)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F,
                 SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, None)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glBindTexture(GL_TEXTURE_2D, 0)

    # Attach depth to framebuffer
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, color_depth_tex, 0)

    draw_buffers = [GL_COLOR_ATTACHMENT1]
    glDrawBuffers(1, draw_buffers)

    # DEPTH 
    depth_tex = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, depth_tex)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F,
                 SCR_WIDTH, SCR_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, None)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glBindTexture(GL_TEXTURE_2D, 0)

    # Attach depth to framebuffer
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0)

    # check for errors (after attaching a texture)
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE):
        print(glCheckFramebufferStatus(GL_FRAMEBUFFER))
        raise RuntimeError("ERROR.FRAMEBUFFER. Framebuffer is not complete!")
    
    # unbind framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0)

Incidentally, if I comment out the fragment shader layout(location = 1) out float depth; and uncomment out float depth and attach my #COLOR_DEPTH as a GL_COLOR_ATTACHMENT0 I do not get the error when I execute glReadPixelsf though I get some strange result.

What I see is the following,

but what I get is

I am not able to explain a) why my image is not 800 x 600 (which is what was specified) b) and why I get my cube repeated c) why the depth is not calculated continuously???

Apologies for the looooong message

Hi!

First of all, I’m a some sort of beginner too. That means I can be wrong, but you have no answers for a some time, I’ll try to help. :slight_smile:

Yes, You must specify exact layout for color (only color) attachments, like those:

layout(location = 0) out vec4 position;
layout(location = 1) out vec4 normal;
layout(location = 2) out vec4 fragColor;
layout(location = 3) out vec4 wPosition;

The rows on your result let me think the image is ok, but was interpreted with height and width incorrectly.

Try to focus on the points where you are working with height and width.

Hope it helps :slight_smile:

Thank you @nimelord nimelord!

I believe what you are referring to applies to the vertex shader not the fragment shader. Yes… I just learned that you can also use layout with fragment shaders. I thought that I was using this correctly but apparently I am not.

Thanks again for trying,

M.

While I have yet to be able to resolve this I found a mistake when using layout() within the fragment shader.

Somehow, I had assumed that the number in the layout() command was related to the number associated with the COLOR_ATTACHMENT. So if I was using COLOR_ATTACHMENT1 I needed to use layout(location =1) but this is incorrect.

The number used with location = is related to the position of the 'COLOR_ATTACHMENTwhen specified using the commandglDrawBuffers. So for instance, if I useCOLOR_ATTACHMENT2` like in the following (Python) example:
glDrawBuffers= (1, [COLOR_ATTACHMENT2])

I need to specify location to be 0 not 2 because there is only one COLOR_ATTACHMENT.

However, even after I corrected for this, I am still getting an error when using glReadPixelsf command.

Partial Success

In order to read pixels from the COLOR_ATTACHMENT1 one has to use glReadBuffer(COLOR_ATTACHMENT1) before!

There are still several results I cannot figure out: why the cube appears repeated, why the results appear with linear gaps.

I am going to call it quits as I resolved the first question I posted. Just to summarize what I learned. When using a framebuffer with various COLOR_ATTACHEMENTs.

One has to use glDrawBuffers() command to tell OpenGL about the different COLOR_ATTACHEMENTs. The order of these attachments is important as this dictates their location when we specify them in a fragment shader (i.e. when we are specifying within the fragment shader where we want to write our results. This is done using the layout() command.

Finally, if we want to read the values from a particular COLOR_ATTACHEMENT using glReadPixels, we previously need to specify this attachment using the command glReadBuffer(GL_COLOR_ATTACHMENT#)