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

Posted

技术标签:

【中文标题】可以从 MSAA FBO 读取单个样本吗?【英文标题】:Possible to read a single sample from MSAA FBO? 【发布时间】:2018-12-15 18:58:08 【问题描述】:

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

  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),只有绘图场景缓冲区和整数缓冲区。在我绘制完所有内容(片段着色器为每个像素设置索引)之后,我首先将整数 ID 缓冲区(GL_COLOR_ATTACHMENT1)读取到单个采样的 FBO,以便从中获取 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);

我的问题是,当我进行 blit 时,我想要的像素的多样本被插入到我要读取的单个像素中。我想要它用于我用来绘制场景的颜色缓冲区,但我不希望它用于我读取的整数 ID。如果我正在读取一个包含 ID 为 50 和 ID 为 100 的片段的像素,我想读取 50 或 100(不在乎哪个)。但是我得到的是 50 到 100 之间的某个值,比如 75。75 实际上可能是一个完全不同的像素,所以我根本不想要那个。

我可以做些什么来读取整数 ID 的单个样本而不是多个样本的插值?

【问题讨论】:

寻找 glSampleMaski() 的用法。 @ParitoshKulkarni 不确定这对我有帮助。看起来这只影响着色器渲染到缓冲区中。由于我的着色器一次渲染场景和整数 ID,如果我使用 glSampleMaski,它会阻止我的整数 ID 插值,但它也会阻止我的场景抗锯齿。我错过了什么吗? 【参考方案1】:

您可以在渲染到纹理通道中实现自己的多采样分辨率,而不是通过位图解析多采样纹理。您可以使用sampler2DMS 类型的采样器,并使用此texelFetch( variant

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

所以P 是二维非标准化纹理坐标,sample 是样本的 ID。如果你真的不关心你得到了哪些值,你可以一直使用 sample 0 。但是您也可以例如遍历所有样本并获取出现次数最多的样本,或者任何适合您需要的样本。

为此,您必须从用于 ID 附件的渲染缓冲区切换到多重采样 2D 纹理。

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

【讨论】:

“具有多重采样 ID 纹理的全屏渲染通道”是什么意思?这是否意味着为此创建一个着色器并以某种方式将 MSAA ID 纹理传递给它? 是的,您需要一个自定义着色器。 好的,这听起来像是我正在寻找的。如何将渲染缓冲区传递给着色器?有 glDraw 调用吗? 像任何其他纹理一样:你将它绑定到某个纹理单元,你只需要使用目标GL_TEXTURE_2D_MULTISAMPLE @Dtor: "如何将渲染的缓冲区传递给着色器?" 你没有;您必须使用 纹理,而不是渲染缓冲区。

以上是关于可以从 MSAA FBO 读取单个样本吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用单个 FBO 将单个场景从多个视点绘制到多个纹理中(每个视点 1 个)仅产生一个有效纹理

GLSL:无法从 FBO 读取纹理并使用片段着色器渲染到另一个 FBO

单个管道可以被多个进程连接和读取吗

纹理可以绑定到多个 fbo 吗?

OpenGL ES 学习教程(十五) 从 颜色缓冲区(FBO的颜色附着)中 读取颜色数据 保存到图片(FreeImage)

如何使用 fread 和 fwrite 从文件中读取 pcm 样本?