如何使用 QOpenGLWidget 渲染文本

Posted

技术标签:

【中文标题】如何使用 QOpenGLWidget 渲染文本【英文标题】:How to render text with QOpenGLWidget 【发布时间】:2015-01-29 13:30:30 【问题描述】:

在旧版本的 Qt 中有 QGLWidget,有一个很好的函数叫做 renderText。现在我正在使用QOpenGLWidget 类,并且缺少呈现文本的功能。

有没有使用 QOpenGLWidget 渲染文本的简单方法?我不喜欢从头开始用 OpenGL 构建整个文本渲染......

【问题讨论】:

code.google.com/p/freetype-gl 【参考方案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();

【讨论】:

我在我的博客上发布了更多相关信息:nils.schimmelmann.us/post/134971073937/… 你必须将你的 QColor 参数乘以 255【参考方案2】:

您可以基于旧的 Qt 源代码自行实现此功能。

在继承自 QOpenGLWidget 的 OpenGL 小部件类(在本例中为 GLBox)中,您必须实现以下方法:

渲染文本:

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)

你不必再担心这个了。

【讨论】:

【参考方案3】:

由于您似乎想要绘制 2D 文本,请使用 QPainter::drawText()。有关在 QOpenGLWidget 上使用 QPainter 的信息,请参阅 here。要在 QOpenGLWidgets 上使用抗锯齿进行文本渲染,请参阅 here。 如果你想绘制 2.5D 文本(2D 文本随着 3D 场景移动),滚动你自己的类并不是“太难”。使用 QFont 和 QFontMetricsF 为您的字体字形构建纹理,为每个字形构建一些四边形到 VBO 中,并为字符串中的字形绘制正确的四边形...

【讨论】:

【参考方案4】:

QOpenGLWidget 上的 QPainter::drawText 依赖于将 GL_UNPACK_ALIGNMENT 设置为 4,否则字符看起来会损坏/乱码。

如果您在应用程序中遇到此问题,请务必致电

glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

在绘制文本之前。 (更多信息请参见https://bugreports.qt.io/browse/QTBUG-65496)

【讨论】:

【参考方案5】:

供遇到类似问题的任何人将来参考 - 我遇到了与Urs's answer 中所述类似的问题:当在带有drawText 的 OpenGL 小部件上显示文本时,缺少一些字母,有时是整个单词。然而,提到的glPixelStorei 调用并没有改变任何东西。

我在阅读QOpenGLWidget painting documentation 时注意到与QOpenGLFunction 相关的内容——实际上,在我们的案例中受影响的小部件还没有使用QOpenGLFunction,但显然直接调用了OpenGL 函数——这导致了奇怪的文本显示。

QOpenGLFunction 派生并在initializeGL 中调用initializeOpenGLFunctions 似乎已经解决了问题。

编辑:不走运 - 我的问题似乎是间歇性的,只是偶尔出现(每次运行程序时都无法重现)。我想接下来我会研究 Qt OpenGL 上下文调试。

【讨论】:

以上是关于如何使用 QOpenGLWidget 渲染文本的主要内容,如果未能解决你的问题,请参考以下文章

添加到 QGraphicsScene 时如何更新 OpenGL 绘图(QOpenGLWidget)?

如何在 PyQt5 QOpenGLWidget 中对球体进行纹理处理

使用 QOpenGLWidget 进行屏幕外渲染的最简单方法

在QOpenGLWidget上使用Qt5小部件而不重写?

如何在 Android 上使用 QOpenGLWidget?

qt glsl渲染rgb