Search code examples
opengl-3

How to view a renderbuffer of GLuints on the screen?


To get a sort of index of the elements drawn on the screen, I've created a framebuffer that will draw objects with solid colors of type GL_R32UI.

The framebuffer I created has two renderbuffer attached. One of color and one of depth. Here is a schematic of how it was created using python:

my_fbo = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER, my_fbo)

rbo = glGenRenderbuffers(2) # GL_DEPTH_COMPONENT16 and GL_COLOR_ATTACHMENT0

glBindRenderbuffer(GL_RENDERBUFFER, rbo[0])
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo[0])

glBindRenderbuffer(GL_RENDERBUFFER, rbo[1])
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI, width, height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo[1])

glBindRenderbuffer(GL_RENDERBUFFER, 0)        
glBindFramebuffer(GL_FRAMEBUFFER, 0)

I read the indexes with readpixel like this:

glBindFramebuffer(GL_FRAMEBUFFER, my_fbo)
glReadPixels(x, y, threshold, threshold, GL_RED_INTEGER, GL_UNSIGNED_INT, r_data)
glBindFramebuffer(GL_FRAMEBUFFER, 0)

The code works perfectly, I have no problem with that.

But for debugging, I'd like to see the indexes on the screen

With the data obtained below, how could I see the result of drawing the indices (unsigned int) on the screen?*

active_fbo = glGetIntegerv(GL_FRAMEBUFFER_BINDING)
my_indices_fbo = my_fbo
my_rbo_depth = rbo[0]
my_rbo_color = rbo[1]

## how mix my_rbo_color and cur_fbo??? ##

glBindFramebuffer(gl.GL_FRAMEBUFFER, active_fbo)

Solution

  • glBlitFramebuffer transfer a rectangle of pixel values from one region of a read framebuffer to another region of a draw framebuffer.

     glBindFramebuffer( GL_READ_FRAMEBUFFER, my_fbo  );
     glBindFramebuffer( GL_DRAW_FRAMEBUFFER, active_fbo );
     glBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST );
    

    Note, you have to be careful, because an GL_INVALID_OPERATION error will occur, if the read buffer contains unsigned integer values and any draw buffer does not contain unsigned integer values. Since the internal format of the frame buffers color attachment is GL_R32UI, and the internal format of the drawing buffer is usually something like GL_RGBA8, this maybe not works, or it even will not do what you have expected.

    But you can create a frame buffer with a texture attached to its color plane an use the texture as an input to a post pass, where you draw a quad over the whole canvas.

    First you have to create the texture with the size as the frame buffer:

    ColorMap0 = glGenTextures(1);
    glBindTexture(GL_TEXTURE_2D, ColorMap0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, width, height, 0, GL_R, GL_UNSIGNED_INT, 0);
    

    You have to attach the texture to the frame buffer:

    my_fbo = glGenFramebuffers(1)
    glBindFramebuffer(GL_FRAMEBUFFER, my_fbo)
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ColorMap0, 0);
    

    When you have drawn the scene then you have to release the framebuffer.

    glBindFramebuffer(GL_FRAMEBUFFER, 0)
    

    Now you can use the texture as an input for a final pass. Simply bind the texture, enable 2D textures and draw a quad over the whole canvas. The quad should range from from (-1,-1) to (1,1), with texture coordinates in range from (0, 0) to (1, 1).
    Of course you can use a shader, with a texture sampler uniform in the fragment shader, for that. You can read the texel from the texture a write to the fragment in an way you want.

    Extension to the answer

    If performance is not important, then you can convert the buffer on the CPU and draw it on the canvas, after reading the frame buffer with glReadPixels.
    For that you can leave your code as it is and read the frame buffer with glReadPixels, but you have to convert the buffer to a format appropriate to the drawing buffer. I suggest to use the internal format GL_RGBA8 or GL_RGB8.
    You have to create a new texture with the convert buffer data.

    debugTexturePlane = ...; 
    debugTexture = glGenTextures(1);
    glBindTexture(GL_TEXTURE_2D, debugTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, debugTexturePlane);
    

    From now on you have 2 possibilities.
    Either you create a new frame buffer and attach the texture to its color plane

    debugFbo = glGenFramebuffers(1)
    glBindFramebuffer(GL_FRAMEBUFFER, debugFbo)
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, debugTexture, 0);
    

    and you use glBlitFramebuffer as described above to copy from the debug frame buffer to the color plane. This should not be any problem, because the internal formats of the buffers should be equal.

    Or you draw a textured quad over the whole viewport. The code may look like this (old school):

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, debugTexture);
    
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0);
    glTexCoord2f(0.0, 1.0); glVertex2f(-1.0,  1.0);
    glTexCoord2f(1.0, 1.0); glVertex2f( 1.0,  1.0);
    glTexCoord2f(1.0, 0.0); glVertex2f( 1.0, -1.0);
    glEnd();