How to render text with QOpenGLWidget

2020-07-03 04:11发布

In older version of Qt there was QGLWidget, with a nice function called renderText. Now I'm using QOpenGLWidget class and the functionality for rendering text is missing.

Is there a easy way to render text using QOpenGLWidget? I won't like to build the whole text rendering with OpenGL from scratch...

标签: c++ qt
4条回答
做自己的国王
2楼-- · 2020-07-03 04:26

As you seem to be wanting to draw 2D text, use QPainter::drawText(). See here for info about using QPainter on a QOpenGLWidget. For using antialiasing for text rendering on QOpenGLWidgets see here.
If you want to draw 2.5D text (2D text moving with the 3D scene) it is not "too hard" to roll your own classes. Use QFont and QFontMetricsF to build a texture for your font glyphs, build some quads for each glyph into a VBO and draw the proper quads for the glyphs in a string...

查看更多
Deceive 欺骗
3楼-- · 2020-07-03 04:26

QPainter::drawText on a QOpenGLWidget relies on GL_UNPACK_ALIGNMENT being set to 4, otherwise characters will look corrupt/scrambled.

If you have this problem in your application, make sure to call

glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

before drawing the text. (see https://bugreports.qt.io/browse/QTBUG-65496 for more info)

查看更多
小情绪 Triste *
4楼-- · 2020-07-03 04:27

You can implement this functionality by yourself based on the old Qt source code.

In your OpenGL widget class inherited from QOpenGLWidget (in this example it's GLBox) you have to implement the following methods:

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

project:

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

and finally 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
}

If you need renderText() because you have to port your Qt4 application to Qt5 just make sure to change the signature of the function provided here to

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

and you don't have to worry about this anymore.

查看更多
家丑人穷心不美
5楼-- · 2020-07-03 04:33

I ended up doing a solution similar to what @jaba wrote. I also noticed some graphical corruption unless I called painter.end() at the end of the method.

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();
}
查看更多
登录 后发表回答