可以从MSAA FBO读取单个样本吗?

时间:2018-12-15 18:58:08

标签: opengl glsl fbo glreadpixels msaa

使用OpenGL绘制对象,并使我的片段着色器输出标量整数ID。为了绘制对象,我使用多重采样进行抗锯齿,因此,当我为整数ID创建缓冲区时,为了使FBO完整,还必须将其创建为MSAA缓冲区:

  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);

您可以在上面的代码中看到,我有2个FBO。第一个是MSAA,具有用于绘制场景的缓冲区,深度缓冲区和ID的整数缓冲区。第二个FBO是单采样的(非MSAA),仅具有绘制场景缓冲区和整数缓冲区。绘制完所有内容(片段着色器为每个像素设置indeces)之后,我首先将其拖到单个采样的FBO中,以读取整数ID缓冲区(GL_COLOR_ATTACHMENT1),以便可以从中获取glReadPixels。在此特定代码中,我只是读取鼠标指向的1个像素:

  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);

我的问题是,当我进行混色处理时,我想要的像素的多个样本会插值到我要读取的单个像素中。我想要用于绘制场景的颜色缓冲区,但我不需要读取的整数ID。如果我正在读取包含ID为50和ID为100的片段的像素,则我想读取50还是100(不在乎)。但是我得到的是介于50到100之间的某个值,例如75。75实际上可能是一个完全不同的像素,所以我根本不想要那个。

是否可以做一些事情来读取整数ID的单个样本,而不是对多个样本进行插值?

1 个答案:

答案 0 :(得分:2)

您可以通过渲染到纹理的过程来实现自己的多采样分辨率,而不必通过斑点处理来解决多采样纹理。您可以使用sampler2DMS类型的采样器,并使用以下texelFetch( variant

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

所以P是2D未归一化的纹理像素坐标,而sample是样本的ID。如果您真的不关心要获得哪个值,则可以一直使用样本0。但是,例如,您还可以遍历所有样本,并选择出现次数最多的样本,或者任何适合您需要的样本。

要执行此操作,您必须从ID附件的渲染缓冲区切换到多采样2D纹理。

因此,基本上,您可以将非多次采样的FBO绑定为绘制FBO,对深度和颜色纹理进行标准blit,并使用多次采样的ID纹理进行全屏渲染,写入非多次采样的ID颜色附件。