Qt:QOpenGLWidget 中的文本渲染
Qt: text rendering in QOpenGLWidget
我想在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 中?
确保您使用支持抗锯齿多重采样的表面格式创建 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);
然后绘制您的文字。
重新实现旧的QOGLWidget 的renderText() 以方便文本渲染。
这样文本会随着场景移动。如果你想要一个静态版本,你可以简化它。
使用以下方法从 QOpenGLWidget(在本例中为 GLBox)继承了 class:
渲染文本:
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
}
给painter加一点旋转好像可以增加文字的流畅度,但是画质有点一般:
painter.rotate(0.01);
painter.drawText(textPosX, textPosY, text);
我在混淆 QPainter
和 QOpenGLWidet
时遇到了一些麻烦,特别是使用我的 QOpenGLWidget
作为绘图设备来呈现单个文本元素时。结果我得到了全屏清晰的颜色。我以前的所有代码似乎都被破坏了。
所以我想出了一个不同的解决方案,使用 QGraphicsScene
作为绘画设备。随后,我将 painter 渲染成 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);
}
我想在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 中?
确保您使用支持抗锯齿多重采样的表面格式创建 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);
然后绘制您的文字。
重新实现旧的QOGLWidget 的renderText() 以方便文本渲染。 这样文本会随着场景移动。如果你想要一个静态版本,你可以简化它。
使用以下方法从 QOpenGLWidget(在本例中为 GLBox)继承了 class:
渲染文本:
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
}
给painter加一点旋转好像可以增加文字的流畅度,但是画质有点一般:
painter.rotate(0.01);
painter.drawText(textPosX, textPosY, text);
我在混淆 QPainter
和 QOpenGLWidet
时遇到了一些麻烦,特别是使用我的 QOpenGLWidget
作为绘图设备来呈现单个文本元素时。结果我得到了全屏清晰的颜色。我以前的所有代码似乎都被破坏了。
所以我想出了一个不同的解决方案,使用 QGraphicsScene
作为绘画设备。随后,我将 painter 渲染成 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);
}