Как визуализировать текст с помощью QOpenGLWidget

В более старой версии Qt был QGLWidget, с красивой функцией renderText. Теперь я использую класс QOpenGLWidget, и функциональность для текста рендеринга отсутствует.

Есть ли простой способ визуализации текста с помощью QOpenGLWidget? Мне не нравится создавать весь текст с OpenGL с нуля...

Ответ 1

В итоге я сделал решение, подобное тому, что написал @jaba. Я также заметил некоторое графическое повреждение, если только я не назвал painter.end() в конце метода.

void MapCanvas::renderText(double x, double y, double z, const QString &str, const QFont & font = QFont()) {
    // Identify x and y locations to render text within widget
    int height = this->height();
    GLdouble textPosX = 0, textPosY = 0, textPosZ = 0;
    project(x, y, 0f, &textPosX, &textPosY, &textPosZ);
    textPosY = height - textPosY; // y is inverted

    // Retrieve last OpenGL color to use as a font color
    GLdouble glColor[4];
    glGetDoublev(GL_CURRENT_COLOR, glColor);
    QColor fontColor = QColor(glColor[0], glColor[1], glColor[2], glColor[3]);

    // Render text
    QPainter painter(this);
    painter.setPen(fontColor);
    painter.setFont(font);
    painter.drawText(textPosX, textPosY, text);
    painter.end();
}

Ответ 2

Как вам кажется, вам нужно рисовать 2D-текст, используйте QPainter:: drawText(). См. здесь для получения информации об использовании QPainter в QOpenGLWidget. Для использования сглаживания для текстового рендеринга в QOpenGLWidgets см. здесь.
Если вы хотите нарисовать текст 2.5D (2D-текст, перемещающийся с 3D-сценой), не слишком сложно катить свои классы. Используйте QFont и QFontMetricsF для создания текстуры для ваших глифов шрифтов, создайте некоторые квадратики для каждого глифа в VBO и нарисуйте правильные квадратики для глифов в строке...

Ответ 3

Вы можете реализовать эту функциональность самостоятельно на основе старого исходного кода Qt.

В вашем классе виджета OpenGL, унаследованном от QOpenGLWidget (в этом примере это GLBox), вы должны реализовать следующие методы:

renderText:

void GLBox::renderText(D3DVECTOR &textPosWorld, QString text)
{
    int width = this->width();
    int height = this->height();

    GLdouble model[4][4], proj[4][4];
    GLint view[4];
    glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
    glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
    glGetIntegerv(GL_VIEWPORT, &view[0]);
    GLdouble textPosX = 0, textPosY = 0, textPosZ = 0;

    project(textPosWorld.x, textPosWorld.y, textPosWorld.z, 
                &model[0][0], &proj[0][0], &view[0],
                &textPosX, &textPosY, &textPosZ);

    textPosY = height - textPosY; // y is inverted

    QPainter painter(this);
    painter.setPen(Qt::yellow);
    painter.setFont(QFont("Helvetica", 8));
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
    painter.drawText(textPosX, textPosY, text); // z = pointT4.z + distOverOp / 4
    painter.end();
}

Проект:

inline GLint GLBox::project(GLdouble objx, GLdouble objy, GLdouble objz,
    const GLdouble model[16], const GLdouble proj[16],
    const GLint viewport[4],
    GLdouble * winx, GLdouble * winy, GLdouble * winz)
{
    GLdouble in[4], out[4];

    in[0] = objx;
    in[1] = objy;
    in[2] = objz;
    in[3] = 1.0;
    transformPoint(out, model, in);
    transformPoint(in, proj, out);

    if (in[3] == 0.0)
        return GL_FALSE;

    in[0] /= in[3];
    in[1] /= in[3];
    in[2] /= in[3];

    *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
    *winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;

    *winz = (1 + in[2]) / 2;
    return GL_TRUE;
}

и, наконец, transformPoint:

inline void GLBox::transformPoint(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
{
#define M(row,col)  m[col*4+row]
    out[0] =
        M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
    out[1] =
        M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
    out[2] =
        M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
    out[3] =
        M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
#undef M
}

Если вам нужен renderText(), потому что вам нужно портировать приложение Qt4 в Qt5, просто убедитесь, что вы изменили подпись функции, представленной здесь, на

void GLBox::renderText(double x, double y, double z, const QString & str, const QFont & font = QFont(), int listBase = 2000)

и вам больше не придется об этом беспокоиться.