1
votes

As part of a 3D mesh viewer I am making in QT with QOpenGLWidget, I need to provide the ability for a user to click a node within the model. To restrict selection to visible nodes only, I have tried to include glReadPixels (GL_DEPTH_COMPONENT) in my selection algorithm.

My problem is that glReadPixels(depth) always returns 0. All the error outputs in the code below return 0 as well. glReadPixels(red) returns correct values:

    GLenum err = GL_NO_ERROR;
    QTextStream(stdout) << "error before reading gl_red = " << err << endl;
    GLfloat winX, winY, myred, mydepth;
    winX = mousex;
    winY = this->height() - mousey;
    glReadPixels(winX,winY,1,1,GL_RED,GL_FLOAT, &myred);
    QTextStream(stdout) << "GL RED = " << myred << endl;
    err =  glGetError();
    QTextStream(stdout) << "error after reading gl_red = " << err << endl;
    glReadPixels(winX,winY,1,1,GL_DEPTH_COMPONENT,GL_FLOAT, &mydepth);
    QTextStream(stdout) << "GL_DEPTH_COMPONENT = " << mydepth << endl;
    err =  glGetError();
    QTextStream(stdout) << "error after reading gl_depth = " << err << endl;

My normal 3D rendering is working fine, I have glEnable(GL_DEPTH_TEST) in my initializeGL() function. At the moment I'm not using any fancy VBOs or VAOs. FaceMeshQualityColor and triVertices are both datatype QVector<QVector3D>. My current face rendering follows the following progression:

shader = shaderVaryingColor;
shader->bind();
shader->setAttributeArray("color", FaceMeshQualityColor.constData());
shader->enableAttributeArray("color");
shader->setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);
shader->setAttributeArray("vertex", triVertices.constData());
shader->enableAttributeArray("vertex");
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1,1);
glDrawArrays(GL_TRIANGLES, 0, triVertices.size());
glDisable(GL_POLYGON_OFFSET_FILL);
shader->disableAttributeArray("vertex");
shader->disableAttributeArray("color");
shader->release();

In my main file I explicitly set my OpenGL version to something with glReadPixels(GL_DEPTH_COMPONENT) functionality (as opposed to OpenGL ES 2.0):

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSurfaceFormat format;
    format.setVersion(2, 1);
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setDepthBufferSize(32);
    QSurfaceFormat::setDefaultFormat(format);
    MainWindow w;
    w.showMaximized();
    return a.exec();
}

Is my problem of glReadPixels(depth) not working somehow related to my treatment of my depth buffer?

Do I need to 'activate' the depth buffer to be able to read from it before I call glReadPixels? Or do I need to have my vertex shader explicitly write depth location to some other object?

1

1 Answers

1
votes

QOpenGLWidget works into an underlying FBO and you can't simply read depth component from that FBO if multi sampling is enabled. The easiest solution is to set the samples to zero, so your code will look like this:

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

format.setSamples(0);

QSurfaceFormat::setDefaultFormat(format);

Or you can use multisampling, but an additional FBO will be required without multisampling where the depth buffer can be copied.

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, ctx->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();
}
};