openglglslfboglreadpixelsmsaa

Possible to read a single sample from MSAA FBO?


Using OpenGL to draw objects and also have my fragment shader outputting a scalar integer ID. For drawing the objects, I'm using multi-sampling for anti-aliasing, so when I create the buffer for the integer ID, I have to create it as an MSAA buffer as well for the FBO to be complete:

  glBindRenderbuffer(GL_RENDERBUFFER, rboColorId);
  glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaSamples, GL_RGBA8,
                        cam.getWidth(), cam.getHeight());
  glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId);
  glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaSamples, GL_DEPTH_COMPONENT,
                        cam.getWidth(), cam.getHeight());
  glBindRenderbuffer(GL_RENDERBUFFER, rboObjId);
  glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaSamples, GL_R32UI,
                        cam.getWidth(), cam.getHeight());
  glBindRenderbuffer(GL_RENDERBUFFER, rboColorNoMsaaId);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
                        cam.getWidth(), cam.getHeight());
  glBindRenderbuffer(GL_RENDERBUFFER, rboObjNoMsaaId);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_R32UI,
                        cam.getWidth(), cam.getHeight());

  glBindRenderbuffer(GL_RENDERBUFFER, 0);
  glBindFramebuffer(GL_FRAMEBUFFER, fboId);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboColorId);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_RENDERBUFFER, rboDepthId);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, rboObjId);
  glBindFramebuffer(GL_FRAMEBUFFER, fboNoMsaaId);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboColorNoMsaaId);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, rboObjNoMsaaId);

As you can see in the code above, I have 2 FBOs. The first is MSAA and has a buffer for drawing the scene, a depth buffer, and an integer buffer for the IDs. The second FBO is single sampled (non-MSAA) and has just the draw scene buffer and the integer buffer. After I draw everything (fragment shader sets indeces for each pixel), I read the integer ID buffer (GL_COLOR_ATTACHMENT1) by first blitting it to the single sampled FBO so I can glReadPixels from it. In this particular code, I'm just reading the 1 pixel where the mouse is pointing:

  glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
  glReadBuffer(GL_COLOR_ATTACHMENT1);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboNoMsaaId);
  glDrawBuffer(GL_COLOR_ATTACHMENT1);
  glBlitFramebuffer(mouse_x_pos, cam.getHeight() - mouse_y_pos, mouse_x_pos+1, cam.getHeight() - mouse_y_pos + 1,
                    0, 0, 1, 1,
                    GL_COLOR_BUFFER_BIT, GL_NEAREST);
  glBindFramebuffer(GL_READ_FRAMEBUFFER, fboNoMsaaId);
  glReadBuffer(GL_COLOR_ATTACHMENT1);
  GLuint objectId;
  glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &objectId);

My problem is that when I blit, the multi-samples for the pixel I want are interpolated into the single pixel I get to read. I want that for the color buffer I'm using to draw the scene, but I do not want that for the integer IDs I read. If I'm reading a pixel that contains fragments for both an ID of 50 and an ID of 100, I want to read either 50 or 100 (don't care which). But what I get is some value between 50 and 100, like 75. 75 may actually be a completely different pixel, so I don't want that at all.

Is there something I can do to read a single sample for the integer ID instead of the interpolation of multiple samples?


Solution

  • Instead of resolving the multisample texture by blitting, you can implement your own multisample resolution in a render-to-texture pass. You can use a sampler of type sampler2DMS, and use this texelFetch( variant:

    gvec4 texelFetch( gsampler2DMS sampler, ivec2 P, int sample);
    

    so P are the 2D unnormalized texel coordinates, and sample is the ID of the sample. If you really don't care about which of the values you get, you can just use sample 0 all the time. But you could also for example iterate over all samples and take the one with the most occurences, or whatever suits your needs.

    For this to work, you will have to switch from a renderbuffer for the ID attachment to a multisampled 2D texture.

    So basically, you can bind the non-multisampled FBO as draw FBO, do a standard blit for depth and color textures, and do a fullscreen render pass with the multisampled ID texture, writing to the non-multisampled ID color attachment.