将渲染目标添加到 QOpenGLWidget 的默认帧缓冲区

Add render target to default framebuffer of QOpenGLWidget

我想将第二个渲染目标添加到 QOpenGLWidget 的默认帧缓冲区。

原因是我想通过将分割掩码渲染到gl_FragData[1]中来实现对象拾取并检查用户是否击中对象。不幸的是,您只能从小部件中检索 GLuint 句柄,并且没有接受句柄的 QOpenGLFramebufferObject 的构造函数,也没有其他选项来检索帧缓冲区。

是否可以在没有解决方法的情况下将另一个纹理附加到小部件的默认帧缓冲区?

我能想到的只有两个选项:

1. 使用本机 OpenGL 调用附加纹理(我宁愿坚持使用纯 Qt),就像在初始化中一样(当然我会存储 segmentationTexture 以便以后能够删除它):

glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
QOpenGLTexture *segmentationTexture = new QOpenGLTexture(QOpenGLTexture::BindingTargetBuffer);
// set texture parameters
segmentationTexture.create();
segmentationTexture.bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, segmentationTexture.textureId(), 0);
segmentationTexture.release();

然后在 paintGL()

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

在 OpenGL 绘制调用之前使用 glReadBuffer(GL_COLOR_ATTACHMENT1);gl_FragData[1] 检索内容。或者,如果这不起作用,则仅使用本机 OpenGL 代码来生成纹理。

2. 创建第二个帧缓冲区对象,将其绑定在 paintGL() 中,然后使用 glBlitFramebuffer(考虑多重采样)将内容与默认帧缓冲区交换以显示渲染,但使用第二个帧缓冲区从 gl_FragData[1]。不过这个感觉有点"nasty".

I'd like to add a second render target to the default framebuffer of a QOpenGLWidget.

答案很简单,您不能向任何 OpenGL 默认帧缓冲区对象添加第二个颜色附件。

OpenGL 4.6 API Compatibility Profile Specification; 2.1. EXECUTION MODEL; page 9

There are two classes of framebuffers: a window system-provided framebuffer associated with a context when the context is made current, and application-created framebuffers. The window system-provided framebuffer is referred to as the default framebuffer. Application-created framebuffers, referred to as framebuffer objects, may be created as desired, A context may be associated with two framebuffers, one for each of reading and drawing operations. The default framebuffer and framebuffer objects are distinguished primarily by the interfaces for configuring and managing their state.

The effects of GL commands on the default framebuffer are ultimately controlled by the window system, which allocates framebuffer resources, determines which portions of the default framebuffer the GL may access at any given time, and communicates to the GL how those portions are structured. Therefore, there are no GL commands to initialize a GL context or configure the default framebuffer.
Similarly, display of framebuffer contents on a physical display device (including the transformation of individual framebuffer values by such techniques as gamma correction) is not addressed by the GL.

OpenGL 4.6 API Compatibility Profile Specification; 9.2. BINDING AND MANAGING FRAMEBUFFER OBJECTS; page 340

Framebuffer objects (those with a non-zero name) differ from the default framebuffer in a few important ways. First and foremost, unlike the default framebuffer, framebuffer objects have modifiable attachment points for each logical buffer in the framebuffer.

对于拾取,您可以只渲染到一个单独的 QOpenGLFramebufferObject,即:

makeCurrent();
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0, 0, width(), height());
QOpenGLFramebufferObject fbo(width(), height(), QOpenGLFramebufferObject::CombinedDepthStencil);
fbo.bind();
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[…] render stuff […]
fbo.release();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glPopAttrib();
QImage fboImage(fbo.toImage());
QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_ARGB32);

另请参阅 toImage docs 解释 2 QImage 的必要性。