OpenGL/GLSL 4.5 - 在默认 FBO 中使用 MRT

问题描述

我正在使用 MRT 来解决 OpenGL 4.5 中的 3D 拾取问题。 (参考链接http://ogldev.atspace.co.uk/www/tutorial29/tutorial29.html

根据我在网上找到的信息,我们在使用MRT时一般会申请一个新的FBO。如果是这样,我应该在3D采摘时对场景进行两次daw(3D采摘的新FBO,以及屏幕的认FBO),这会浪费时间。

所以我想知道是否可以在认 FBO 中使用 MRT,但我遇到了一些问题。

这是我的代码

// apply the texture buffer attached to GL_COLOR_ATTACHMENT1. (GL_COLOR_ATTACHMENT0 is reserved for the screen ?)
glEnable(GL_TEXTURE_2D);
glGenTextures(1,&m_pickingColorTexture);
glBindTexture(GL_TEXTURE_2D,m_pickingColorTexture);
glTexImage2D(GL_TEXTURE_2D,GL_RGB32F,w,h,GL_RGB,GL_FLOAT,nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,m_pickingColorTexture,0);
gldisable(GL_TEXTURE_2D);

// draw code. 
GLenum buffers[] = { GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2,buffers);
glDrawElements(........);

// fragment shader,output two kinds of things: frag_color0 for screen display,frag_color1 for 3D picking
 .......
layout (location = 0) out vec4 frag_color0; 
layout (location = 1) out vec4 frag_color1; 
 
void main() {
     frag_color0 = vec4(...);
     frag_color1 = vec4(...);
}

// query the result
glreadBuffer(GL_COLOR_ATTACHMENT1);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glreadPixels(x,y,1,&pixelInfo);

当我最后查询 GL_COLOR_ATTACHMENT1 时,结果似乎是 frag_color0 而不是 frag_color1。

如果有人能帮我解决问题,我将不胜感激,谢谢!

解决方法

当我最后查询 GL_COLOR_ATTACHMENT1 时,结果似乎是 frag_color0 而不是 frag_color1

不,它不是,唯一的原因是FBO 0 没有GL_COLOR_ATTACHMENT1,因此您无法查询它。 FBO 0 指的是窗口系统提供的帧缓冲区(通过 windows 上的 pixelformat、X11/Unix 上的 visual 或 fbconfig 以及其他特定于平台的方式指定)。

这个帧缓冲区可能有也可能没有深度缓冲区,可能有也可能没有模板缓冲区,可能有也可能没有(旧的,弃用的)累积缓冲区,可能有以下颜色缓冲区 :

  • GL_BACK_LEFT
  • GL_BACK_RIGHT
  • GL_FRONT_LEFT
  • GL_FRONT_RIGHT

这是您所能获得的最大数量,而且前提是您的 GPU 和驱动程序支持用于 3D 立体渲染的“四缓冲立体”模式。在典型情况下,您只有两个:

  • GL_BACK:绘制新帧的缓冲区
  • GL_FRONT:当前显示在屏幕上的缓冲区。

您不能从 GL 内部更改任何这些缓冲区配置(您只能在设置将 GL 上下文连接到的窗口时影响这一点),并且无法将 GL 对象(纹理、渲染缓冲区)与默认帧缓冲区

glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,m_pickingColorTexture,0);

如果在绑定 FBO 0 时调用此方法,只会导致 GL 错误,否则会被忽略。这些情况也是如此:

GLenum buffers[] = { GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2,buffers);
[...]
glReadBuffer(GL_COLOR_ATTACHMENT1);

因此,您要渲染的是 glDrawBuffer 的默认值,在典型情况下为 GL_BACK,您的第二个着色器输出最终无处可去,而您正在回读的是 {{ 1}} 是默认值,在典型情况下也是 glReadBuffer

根据我在网上查到的信息,我们在使用捷运时一般都会申请一个新的FBO。

您在网上找到的信息是正确的。对于 OpenGL 中的 MRT,您需要设置自定义 FBO,并且不能与来自 FBO 0 的缓冲区共享它。(从技术上讲,这并非严格正确,您仍然可以使用诸如 { {1}} 或者如果您有一个支持立体声的 GPU,您甚至可能(误)使用 GL_BACK。但这些的实际用例非常有限)。

如果是这样,我应该在3D采摘过程中绘制两次场景(3D采摘的新FBO,屏幕的默认FBO),这会浪费时间。

不一定是这样。您仍然可以使用 MRT 将其渲染到一些自定义 FBO 中,然后仅 blit 一种颜色附件到 FBO 0 的 {GL_FRONT,GL_BACK} 缓冲区。