无法使用QOpenGLWidget读取GL_DEPTH_COMPONENT

Bas*_*ond 3 c++ opengl qt

我正在为QOpenGLWidget内的3D对象实现简单的单击选择。为此,我需要将2D鼠标坐标转换为3D世界空间。我以前使用QGLWidget实现了整个过程。使用QOpenGLWidget,我无法读取像素的GL_DEPTH_COMPONENT:

float z;
glReadPixels(pixel.x, height - pixel.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
Run Code Online (Sandbox Code Playgroud)

“ z”始终为0。为确保像素坐标正确,我尝试接收GL_RGBA值:

float rgba[4];
glReadPixels((int)p_temp.x(), (int) (viewport[3] - p_temp.y()), 1, 1, GL_RGBA, GL_FLOAT, rgba);
Run Code Online (Sandbox Code Playgroud)

,它返回正确的像素颜色。为了使它起作用,我不得不将像素坐标的域从局部坐标更改为父坐标。这可能是由于GL_VIEWPORT设置为与父窗口小部件尺寸相对应的事实造成的:

int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
Run Code Online (Sandbox Code Playgroud)

对于QGLWidget,它返回: {0, 0, this->width(), this->height()}

对于QOpenGLWidget,它返回: {0, 0, this->parent()->width(), this->parent()->height()}

顺便说一句,我的OpenGL版本是4.5,使用带有GL_DEPTH_COMPONENT的glReadPixels我没有任何OpenGL错误

现在,我对可能会丢失的东西一无所知。有任何想法吗?

小智 5

QOpenGLWidget可以在基础帧缓冲区对象(FBO)中使用,如果启用了多重采样,则不能简单地从该FBO中读取深度分量。最简单的解决方案是将样本设置为零,因此您的代码将如下所示:

QSurfaceFormat format;
format.setVersion(2, 1);
format.setProfile(QSurfaceFormat::CoreProfile);

format.setSamples(0);

QSurfaceFormat::setDefaultFormat(format);
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用诸如format.setSamples(4)之类的多重采样,但是需要额外的FBO,而无需多重采样即可复制深度缓冲区。

class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
//
// OTHER WIDGET RELATED STUFF
//
QOpenGLFramebufferObject *mFBO=nullptr;

MyGLWidget::paintGL()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  //
  // DRAW YOUR SCENE HERE!
  //

  QOpenGLContext *ctx = QOpenGLContext::currentContext();

  // FBO must be re-created! is there a way to reset it?
  if(mFBO) delete mFBO;

  QOpenGLFramebufferObjectFormat format;
  format.setSamples(0);
  format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
  mFBO = new QOpenGLFramebufferObject(size(), format);

  glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFramebufferObject());
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO->handle());
  ctx->extraFunctions()->glBlitFramebuffer(0, 0, width(), height(), 0, 0, mFBO->width(), mFBO->height(), GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST);

  mFBO->bind(); // must rebind, otherwise it won't work!

  float mouseDepth = 1.f;
  glReadPixels(mouseX, mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &mouseDepth);

  mFBO->release();
}
};
Run Code Online (Sandbox Code Playgroud)