QGLWidget отображает текст с неправильной глубиной

Я использую это для рендеринга текста в QGLWidget.

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

Строка отображается на глубине ~0,5 (получается с помощью glReadPixel()).

Однако в моем случае он должен быть ближе к ~0,9. Когда я конвертирую координаты x, y, z в координаты экрана с текущими матрицами, я также нахожу результат ~ 0,9.

Почему такая разница? Это заставляет текст всегда появляться впереди.

Я создал простой проект QT в Visual Studio, чтобы воспроизвести проблему. Он рисует зеленый квадрат с текстом впереди и позади квадрата. Оба текста выглядят перед квадратом. А глубину пикселя можно прочитать по наведению мышки.

Я использую Qt версии 5.5, созданную для 64-битной платформы.

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

рендертексттест.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

рендертексттест.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

person Octo    schedule 21.07.2015    source источник
comment
Я также заметил, что глубина пикселей текста увеличивается по мере приближения текста к камере. Должно быть наоборот.   -  person Octo    schedule 22.07.2015
comment
Обновлено описание с тест-кейсом.   -  person Octo    schedule 22.07.2015
comment
По-видимому, это известная ошибка QT. Все еще нерешенный. bugreports.qt.io/browse/QTBUG-31156 bugreports.qt.io/browse/QTBUG-42838   -  person Octo    schedule 23.07.2015
comment
Добавлен проект тестового примера в средство отслеживания ошибок QT bugreports.qt.io/browse/QTBUG-31156   -  person Octo    schedule 23.07.2015


Ответы (2)


Там может быть 3 проблемы

1- целевая глубина корректно вычисляется в QGLWidget::renderText() и передается в paintEngine через setTranslateZ(). Однако вершинный шейдер не устанавливает это значение непосредственно в координатах обрезанных вершин. Вместо этого он переводит это значение.

2- знак translateZ кажется неверным. Это объясняет, почему глубина текстовых пикселей увеличивается, когда текст приближается.

3- кажется, что обрезанные координаты [0, 1] отображаются в диапазоне [0,5, 1]. Однако я не видел никакого вызова glDepthRange() в исходном коде Qt.

Если мы изменим код qglslComplexGeometryPositionOnlyVertexShader в qglengineshadersource_p.h следующим образом, это решит проблему.

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";
person Octo    schedule 23.07.2015
comment
Опубликовал этот обходной путь/исправление в качестве предложения разработчикам QT в системе отслеживания ошибок. - person Octo; 11.08.2015

Я обнаружил, что хорошим обходным путем является преобразование текста в текстуру. А затем отобразите эту текстуру на сцене.

Это хорошо работает, если текст отображается на сплошном фоне. QGLWidget, похоже, не пишет в альфа-канал.

person Octo    schedule 11.08.2015