无法使用 QOpenGLWidget 读取 GL_DEPTH_COMPONENT

Posted

技术标签:

【中文标题】无法使用 QOpenGLWidget 读取 GL_DEPTH_COMPONENT【英文标题】:Cannot read GL_DEPTH_COMPONENT using QOpenGLWidget 【发布时间】:2016-02-09 23:03:37 【问题描述】:

我正在为 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);

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

,返回正确的像素颜色。 为了使其工作,我必须将像素坐标的域从本地坐标更改为父坐标。这可能是由于 GL_VIEWPORT 设置为对应于父窗口小部件的大小:

int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);

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

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

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

现在,我对我可能遗漏的东西一无所知。有什么想法吗?

【问题讨论】:

试试 GL_DEPTH_STENCIL 而不是 GL_DEPTH_COMPONENT? 你怎么知道深度没有被读取?你得到了什么价值,你期望得到什么价值?另外,你确定你有一个深度缓冲区吗? @Nicol Bolas 如果您在没有附加深度缓冲区的情况下读取 GL_DEPTH_COMPONENT,OpenGL 不会给出错误吗? 我已经调用glEnable(GL_DEPTH_TEST) 来启用默认深度缓冲区。在尝试读取深度数据后,我检查了glGetError(),它返回了GL_NO_ERROR。有趣的是,即使我注释掉了glEnable(GL_DEPTH_TEST),所以我不确定该函数是否考虑到是否真的有一个深度缓冲区可以从中读取像素数据。如上所述,深度的所有值都是 0,无论我点击的是哪个对象。 @AndrewWilliamson 是的,它应该:“如果格式为 GL_DEPTH_COMPONENT 并且没有深度缓冲区,则生成 GL_INVALID_OPERATION。” 【参考方案1】:

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

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

format.setSamples(0);

QSurfaceFormat::setDefaultFormat(format);

或者您可以使用像 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();

;

【讨论】:

我的问题与 OP 完全相同,但这根本无法解决我的问题。 首先,在您的小部件上调用 show() 之前,尝试在您的 main.cpp 中将 QSurfaceFormat::samples 设置为零。必须这样工作,因为 FBO 不会被多重采样。 khronos.org/opengl/wiki/GL_EXT_framebuffer_multisample

以上是关于无法使用 QOpenGLWidget 读取 GL_DEPTH_COMPONENT的主要内容,如果未能解决你的问题,请参考以下文章

glReadPixels 可以用来从 GL_TEXTURE_3D 读取图层吗?

使用 QOpenGLWidget 中的着色器更新 QImage

无法在 QopenGLWidget 中捕获 QkeyEvent

我无法在 QOpenGLWidget 上绘画

QOpenGLWidget 粉碎显示

Qt OpenGL (QOpenGLWidget) - 简单三角形