0
votes

I use this to render text in a QGLWidget

QGLWidget::renderText(x, y, z, text, font)

The string is rendered at a depth of ~0.5 (obtained via glReadPixel()).

However, in my case it should be closer to ~0.9. When I convert the x,y,z coords into screen coords with the current matrices I also find a result of ~0.9.

Why such a difference? It makes the text appear always in front.

I created a simple QT project in visual studio to reproduce the issue. It draws a green square with text in front and behind the square. Both text look in front of the square. And the depth of the pixel can be read by hovering the mouse.

I use Qt version 5.5 built for 64bit platform.

MyGLWidget.h

#include <QGLWidget>
#include <QMouseEvent>

class MyGLWidget : public QGLWidget
{
Q_OBJECT

private:
    float _depth;

public:
    MyGLWidget(QWidget * parent = 0);
    virtual ~MyGLWidget();

    virtual void initializeGL();
    virtual void paintGL();

    void mouseMoveEvent(QMouseEvent * event);

signals:
    void depthRead(float);
};

MyGLWidget.cpp

#include "MyGLWidget.h"
#include <gl/GLU.h>
#include <Qfont>

MyGLWidget::MyGLWidget(QWidget * parent) : QGLWidget(parent)
{    
}

MyGLWidget::~MyGLWidget()
{    
}

void MyGLWidget::initializeGL()
{    
}

void MyGLWidget::paintGL()
{
    // set up projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float width = this->width();
    float height = this->height();
    glViewport(0, 0, width, height);
    gluPerspective(45, width / height, 1, 100);

    // set up model view
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0,0,5,  // eye
              0,0,0,  // look at
              0,1,0); // up

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);

    // draw a green square
    glColor4f(0,1,0,1);
    glBegin(GL_QUADS);
    glVertex3f(-1,-1,0);
    glVertex3f(1,-1,0);
    glVertex3f(1,1,0);
    glVertex3f(-1,1,0);
    glEnd();

    // render some blue text behind the square
    QFont font;
    font.setPointSize(20);
    glColor4f(0,0,1,1);
    renderText(-2,-0.5,-1, "BEHIND_BEHIND_BEHIND_BEHIND", font);

    // render some red text in front of the square
    glColor4f(1,0,0,1);
    renderText(-2,0.5,+1, "IN_FRONT_IN_FRONT_IN_FRONT_I", font);
}

void MyGLWidget::mouseMoveEvent(QMouseEvent * event)
{
    int x = event->x();

    // flip y for QT origin is top left while OpenGL origin is bottom left
    int y = this->height() - event->y();

    // read pixel depth
    glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &_depth);

    // update ui
    emit depthRead(_depth);
}

rendertexttest.h

#ifndef RENDERTEXTTEST_H
#define RENDERTEXTTEST_H

#include <QtWidgets/QMainWindow>
#include "ui_rendertexttest.h"
#include "MyGLWidget.h"

class RenderTextTest : public QMainWindow
{
    Q_OBJECT

public:
    RenderTextTest(QWidget *parent = 0);
    ~RenderTextTest();

public slots:
    void onDepthRead(float depth);

private:
    Ui::RenderTextTestClass ui;
    MyGLWidget * _glwidget;
};

#endif // RENDERTEXTTEST_H

rendertexttest.cpp

#include "rendertexttest.h"

RenderTextTest::RenderTextTest(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    _glwidget = new MyGLWidget(this);
    _glwidget->setMouseTracking(true);

    QObject::connect(_glwidget, SIGNAL(depthRead(float)),
                     this, SLOT(onDepthRead(float)));

    ui._mainLayout->addWidget(_glwidget);
}

RenderTextTest::~RenderTextTest()
{

}

void RenderTextTest::onDepthRead(float depth)
{
    ui._lblDepth->setText(QString::number(depth));
}

ui_rendertexttest.h

/********************************************************************************
** Form generated from reading UI file 'rendertexttest.ui'
**
** Created by: Qt User Interface Compiler version 5.3.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_RENDERTEXTTEST_H
#define UI_RENDERTEXTTEST_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_RenderTextTestClass
{
public:
    QWidget *centralWidget;
    QWidget *verticalLayoutWidget;
    QVBoxLayout *_mainLayout;
    QLabel *_lblDepth;

    void setupUi(QMainWindow *RenderTextTestClass)
    {
        if (RenderTextTestClass->objectName().isEmpty())
            RenderTextTestClass->setObjectName(QStringLiteral("RenderTextTestClass"));
        RenderTextTestClass->resize(600, 438);
        centralWidget = new QWidget(RenderTextTestClass);
        centralWidget->setObjectName(QStringLiteral("centralWidget"));
        verticalLayoutWidget = new QWidget(centralWidget);
        verticalLayoutWidget->setObjectName(QStringLiteral("verticalLayoutWidget"));
        verticalLayoutWidget->setGeometry(QRect(9, 9, 581, 381));
        _mainLayout = new QVBoxLayout(verticalLayoutWidget);
        _mainLayout->setSpacing(6);
        _mainLayout->setContentsMargins(11, 11, 11, 11);
        _mainLayout->setObjectName(QStringLiteral("_mainLayout"));
        _mainLayout->setSizeConstraint(QLayout::SetDefaultConstraint);
        _mainLayout->setContentsMargins(0, 0, 0, 0);
        _lblDepth = new QLabel(centralWidget);
        _lblDepth->setObjectName(QStringLiteral("_lblDepth"));
        _lblDepth->setGeometry(QRect(10, 410, 581, 16));
        RenderTextTestClass->setCentralWidget(centralWidget);

        retranslateUi(RenderTextTestClass);

        QMetaObject::connectSlotsByName(RenderTextTestClass);
    } // setupUi

    void retranslateUi(QMainWindow *RenderTextTestClass)
    {
        RenderTextTestClass->setWindowTitle(QApplication::translate("RenderTextTestClass", "RenderTextTest", 0));
        _lblDepth->setText(QApplication::translate("RenderTextTestClass", "Depth:", 0));
    } // retranslateUi

};

namespace Ui {
    class RenderTextTestClass: public Ui_RenderTextTestClass {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_RENDERTEXTTEST_H
2
I also noticed that the depth of the text pixels increases as the text gets closer to the camera. It should be the opposite.Octo
Updated description with a test case.Octo
Apparently this is a known QT bug. Still unresolved. bugreports.qt.io/browse/QTBUG-31156 bugreports.qt.io/browse/QTBUG-42838Octo
Added the test case project to the QT bugtracker bugreports.qt.io/browse/QTBUG-31156Octo

2 Answers

0
votes

There might 3 issues

1- the target depth is computed correctly in QGLWidget::renderText() and passed to the paintEngine via setTranslateZ(). However the vertex shader does not set this value directly in the vertex clipped coordinates. Instead it translate by this value.

2- the sign of translateZ seems incorrect. That would explain why the depth of the text pixels increases when the text comes closer.

3- it seems clipped coordinates [0, 1] are mapped to range [0.5 , 1] Yet I did not see any call to glDepthRange() in Qt's source.

If we change the code of qglslComplexGeometryPositionOnlyVertexShader in qglengineshadersource_p.h with the following, it fixes the problem.

static const char* const qglslComplexGeometryPositionOnlyVertexShader = "\n\
    uniform highp mat3 matrix; \n\
    uniform highp float translateZ; \n\
    attribute highp vec2 vertexCoordsArray; \n\
    void setPosition(void) \n\
    { \n\
      vec3 v = matrix * vec3(vertexCoordsArray, 1.0); \n\
      v.z = (-translateZ - 0.5f) * 2.0f; \n\
      gl_Position = vec4(v.xyz, 1.0);\n\
    } \n";
0
votes

I found that a nice workaround is to render the text into a texture. And then display that texture in the scene.

This works nicely if the text is rendered over a solid background color. QGLWidget does not seem to write to the alpha channel.