1

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?

Dtor
  • 590
  • 2
  • 9
  • Look for glSampleMaski() usage. – Paritosh Kulkarni Dec 15 '18 at 19:11
  • @ParitoshKulkarni Not sure that helps me. It looks like that only effects the shader renders into the buffer. Since my shader renders both the scene and the integer ID in one pass, if I use glSampleMaski, it stops my integer ID from interpolating, but it also stops my scene from anti-aliasing. Am I missing something? – Dtor Dec 15 '18 at 19:22

1 Answers1

2

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.

derhass
  • 43,833
  • 2
  • 57
  • 78
  • What do you mean by a 'fullscreen render pass with the multisampled ID texture'? Does this mean create a shader just for this and somehow pass the MSAA ID texture into it? – Dtor Dec 15 '18 at 19:35
  • Yes, you will need a custom shader. – derhass Dec 15 '18 at 19:37
  • Ok, this sounds like what I may be looking for. How do I pass the rendered buffer to the shader? Is there a glDraw call for that? – Dtor Dec 15 '18 at 19:43
  • Like any other texture: you bind it to some texture unit, you just have to use target `GL_TEXTURE_2D_MULTISAMPLE`. – derhass Dec 15 '18 at 19:46
  • 3
    @Dtor: "*How do I pass the rendered buffer to the shader?*" You don't; you have to use a *texture*, not a renderbuffer. – Nicol Bolas Dec 15 '18 at 20:51
  • Took a bit to get it setup, but this does what I need. I was thinking this would be faster than a blit, but I'm seeing the opposite. On an 1858x1177 scene, I'm seeing my shader drop the seconds/frame by about 1.4ms over doing the interpolated blit. Does it make sense that the simple shader would take longer? If it matters, I'm using the shader with a glDrawArrays(GL_TRIANGLE_FAN) call to draw a quad. – Dtor Dec 16 '18 at 23:50
  • 1.4ms does sound a bit expensive for such a simplem pass, but it is hard to tell what exactly is causing this – derhass Dec 17 '18 at 00:47