Qt: 在 QOpenGLWidget 中渲染文本

3
我想在 QOpenGLWidget 上绘制文本标签。我尝试使用 QPainter 完成此任务,但没有成功 - 文本看起来不美观且没有抗锯齿效果。在 Qt OpenGL/2dpainting 示例中,它也看起来很丑陋。然而,在使用 OpenGL 后端的 QML 控件中,文本渲染要好得多。在这里 http://blog.qt.io/blog/2011/07/15/text-rendering-in-the-qml-scene-graph/ 我找到了一种在 QML 中使用的技术。是否有一种方法可以使用该技术在 QOpenGLWidget 中绘制文本?
PS:也许正确的方法是将我的整个场景嵌入到 QQuickWidget 的 QSGSceneGraph 中?
4个回答

2

确保使用支持多重采样的表面格式来创建您的QGL/QopenGLWidget以进行抗锯齿处理:

QSurfaceFormat format = QSurfaceFormat::defaultFormat();
format.setSamples(4);
QOpenGLWidget * glWidget = new QOpenGLWidget;
glWidget->setFormat(format);

然后,在使用QPainter绘制该小部件时,请确保打开抗锯齿功能:

QPainter painter(paintDevice);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

然后对你的文本进行涂色。

1
重新实现旧的QOGLWidget的renderText()方法,方便进行文字渲染。这样文字将随着场景移动。如果您想要一个静态版本,可以简化它。
从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;
}

最后,转换点:
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
}

0

在绘制器中加入一点旋转似乎能让文本更加流畅,但质量有些平庸:

painter.rotate(0.01);
painter.drawText(textPosX, textPosY, text);

0

我在混淆QPainterQOpenGLWidet时遇到了一些麻烦,特别是在使用我的QOpenGLWidget作为绘图设备来渲染单个文本元素时。结果我得到了整个屏幕的清晰颜色。所有以前的代码似乎都失效了。 因此,我想出了一个不同的解决方案,使用QGraphicsScene作为绘图设备。随后,我将绘图器渲染到QImage中,然后可以使用它来创建一个常规的2D-QOpenGLTexture。这个纹理可以像往常一样通过QOpenGLBuffer缓冲区进行绘制。

void MyQOpenGLWidget::createTexture(int x, int y, int z, const QString& str, const QFont& font) {
    // create an image of the text with transparent backround
    QGraphicsScene scene;
    scene.addText(str, font);
    QImage img(scene.width(), scene.height(), QImage::Format_ARGB32_Premultiplied);
    img.fill(QColor(0, 0, 0, 0));
    QPainter painter(&img);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    scene.render(&painter);

    // store the tex as member somewhere in the QOpenGLWidget
    QOpenGLTexture tex(img);

    // store the bufferindex as member somewhere in the QOpenGLWidget
    int bufferIndex = 0
    /*this is the index corresponding to first element of coords writen into buffer
    -> the number of elements within your tex container times SPRITE_BUFFER_SIZE seems to be reasonable*/

    // calculate coordinates 
    GLfloat coords[SPRITE_BUFFER_SIZE];
    /* [SPRITE_BUFFER_SIZE=20=4*5]->4 corner points
    3-dimensions +2 binary (determine corner point)*/

    // write to buffer 
    buffer.bind()
    buffer.write(sizeof(GLfloat)*bufferIndex, coords, SPRITE_BUFFER_SIZE * sizeof(GLfloat))
}

然后在 paintGL 中,我们将绘制创建的纹理

void MyQOpenGLWidget::paintGL() {
    // somewhere within this function
    tex.bind();
    glDrawArrays(GL_TRIANGLE_FAN, bufferIndex / 5, 4);
}

确保您使用正确的混合函数。

void MyQOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    // somewhere within initializeGL
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接