3

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?

Florian Blume
  • 3,237
  • 17
  • 37

1 Answers1

2

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();
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Well, I tried the second part using gl_FragData. But inserting my rendering code that already works after the two clear color calls rendered only the clear color. – Florian Blume Jun 21 '18 at 17:18
  • What you wrote is exactly what I tried, but I must have made some mistake because I tried again and it works. Thanks for all your time and answers! – Florian Blume Jun 21 '18 at 19:29
  • 2
    If anyone else experiences this problem: you must call toImage() like this: fbo.toImage(false, 1); and not fbo.toImage(false, GL_COLOR_ATTACHMENT1);. I'm still learning... – Florian Blume Jun 21 '18 at 20:10