Search code examples
qtopenglframebufferrendertarget

QOpenGLWidget with custom framebuffer and multiple render targets


Related to my other question, I'm trying to render a segmentation mask to enable object picking. But I am not able to achieve the desired result.

Option 1 did not work at all. I was not able to retrieve the content of color attachment 1, or check if it existed at all (I created the attachment using only native OpenGL calls).

Using this post, I was able to reproduce the green.png and red.png images by creating a custom frame buffer with a second color attachment which is then bound and drawn to (all in paintGL()).

Somehow I had to use the person's frame buffer creation code because when I created the frame buffer myself there was always a warning saying toImage called for missing color attachment, although I attached the color attachment and textures() called on the frame buffer returned two objects. I then tried to insert my rendering code after

GLfloat red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
f->glClearBufferfv(GL_COLOR, 0, red);

GLfloat green[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
f->glClearBufferfv(GL_COLOR, 1, green);

but this still resulted in the red and green image. But the code renders fine when using the normal default framebuffer. I adapted the shader to (short version for testing purposes):

void main() {
       gl_FragData[0] = vec4(1.0, 1.0, 1.0, 1.0);
       gl_FragData[1] = vec4(0.0, 0.0, 0.0, 1.0);
}

Since I was able to produce the red and green image, I'm assuming there must be a way to retrieve the frag data with this custom framebuffer. The solution I have right now is a complete (!) copy of the program and another dedicated fragment shader which's sole purpose is to render the segmentation, and perform all OpenGL draw calls a second time. As you can guess, this is a somewhat ugly solution, although the scenery is not that large and my computer is able to handle it easily. Has anyone got an idea/link?


Solution

  • If you want to write to multiple render targets in a Fragment shader, then you have to declare multiple output variables:

    #version 330
    
    layout(location = 0) out vec4 fragData0;
    layout(location = 1) out vec4 fragData1;
    
    void main() 
    {
        fragData0 = vec4(1.0, 1.0, 1.0, 1.0);
        fragData1 = vec4(0.0, 0.0, 0.0, 1.0);
    }
    

    From GLSL version 1.1 (#version 110, OpenGL 2.0) to GLSL version 1.5 (#version 150, OpenGL 3.2), the same can be achieved by writing to the built in fragment shader output variable gl_FragData.

    void main() 
    {
        gl_FragData[0] = vec4(1.0, 1.0, 1.0, 1.0);
        gl_FragData[1] = vec4(0.0, 0.0, 0.0, 1.0);
    }
    

    See also Fragment Shader Outputs - Through The Ages


    To use multiple render targets in Qt, a 2nd color attachment has to be add to the framebuffer and the list of color buffers has to be specified by glDrawBuffers:

    QOpenGLShaderProgram *program;
    QOpenGLFramebufferObject *fb; 
    int fb_width;
    int fb_height,
    

    fb = new QOpenGLFramebufferObject( fb_width, fb_height );
    fb->addColorAttachment( fb_width, fb_height );
    

    glViewport(0, 0, fb_width, fb_height);
    fb->bind();
    glClearColor(0, 0, 0, 1);
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
    GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
    glDrawBuffers(2, buffers);
    
    program->bind();
    // ..... do the drawing    
    
    program->release();
    fb->release();
    

    The OpenGL texture objects which are attached to the framebuffer, can be accessed:

    QVector<GLuint> fb_textrues = fb->textures();