OpenGL绘制文本

一:QPainter绘制

在 OpenGL 渲染的窗口中(如 QOpenGLWidget),通过 QPainter 直接绘制文本。Qt 会自动将 2D 内容(文本、图形)与 OpenGL 内容合成。在paintGL()里面绘制,如果有其他纹理,在绘制纹理后解绑资源,再绘制文本。

复制代码
    m_program.bind();

    // 绑定纹理
    m_texture->bind(0);
    m_program.setUniformValue("texture1", 0);
    // 绘制矩形
    glBindVertexArray(VAO[0]);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    // 解绑VAO
    glBindVertexArray(0);
    m_program.release();


    // ----------------- 绘制文字 -----------------
      QPainter painter(this);
      painter.setRenderHint(QPainter::Antialiasing);
      painter.setPen(Qt::yellow);
      painter.setFont(QFont("Arial", 16, QFont::Bold));

      // 带背景的文字
      QString text =  QString("%1:%2x%3").arg("宽高").arg(width()).arg(height());
      QRect textRect = painter.fontMetrics().boundingRect(text);
      textRect.moveTo(5, 5);
      painter.fillRect(textRect.adjusted(-1, -1, 1, 1), QColor(0, 0, 0, 128));
      painter.drawText(textRect, Qt::AlignLeft, text);
      painter.end();

二:生成文本纹理并渲染四边形(高性能,适合动态文本)

将文本预渲染为纹理,通过 OpenGL 四边形显示,适合高频更新或大量文本。

步骤 1:创建文本纹理
复制代码
QImage MyGLWidget::createTextTexture(const QString& text, int width, int height) {
    QImage image(width, height, QImage::Format_ARGB32);
    image.fill(Qt::transparent);
    
    QPainter painter(&image);
    painter.setPen(Qt::white);
    painter.setFont(QFont("Arial", 24));
    painter.drawText(image.rect(), Qt::AlignCenter, text);
    painter.end();

    // OpenGL 纹理坐标系原点在左下角,需垂直翻转图像
    return image.mirrored(false, true);
}
步骤 2:绑定纹理并渲染四边形
复制代码
QImage MyGLWidget::createTextTexture(const QString& text, int width, int height) {
    QImage image(width, height, QImage::Format_ARGB32);
    image.fill(Qt::transparent);
    
    QPainter painter(&image);
    painter.setPen(Qt::white);
    painter.setFont(QFont("Arial", 24));
    painter.drawText(image.rect(), Qt::AlignCenter, text);
    painter.end();

    // OpenGL 纹理坐标系原点在左下角,需垂直翻转图像
    return image.mirrored(false, true);
}

‌优化技巧‌:

  • 使用 ‌纹理缓存‌ 存储常用文本,避免重复生成。
  • 动态更新纹理时,使用 glTexSubImage2D 局部更新数据。

三、使用 FreeType 库 + OpenGL(灵活但复杂)

通过 FreeType 加载字体文件生成字形纹理图集,实现高度定制的文本渲染(如游戏引擎风格)。

步骤 1:集成 FreeType 库

在 .pro 文件中添加依赖:

LIBS += -lfreetype

步骤 2:加载字体并生成字形
复制代码
#include <ft2build.h>
#include FT_FREETYPE_H

struct Character {
    GLuint textureID;
    glm::ivec2 size;
    glm::ivec2 bearing;
    GLuint advance;
};

std::map<GLchar, Character> characters;

void loadFont(const char* fontPath) {
    FT_Library ft;
    FT_Init_FreeType(&ft);
    
    FT_Face face;
    FT_New_Face(ft, fontPath, 0, &face);
    FT_Set_Pixel_Sizes(face, 0, 48);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 禁用字节对齐限制
    
    for (GLubyte c = 0; c < 128; c++) {
        FT_Load_Char(face, c, FT_LOAD_RENDER);
        GLuint texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RED,
                     face->glyph->bitmap.width,
                     face->glyph->bitmap.rows,
                     0, GL_RED, GL_UNSIGNED_BYTE,
                     face->glyph->bitmap.buffer);
        
        // 设置纹理参数
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        
        Character character = {
            texture,
            glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
            glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
            static_cast<GLuint>(face->glyph->advance.x)
        };
        characters.insert(std::make_pair(c, character));
    }
    
    FT_Done_Face(face);
    FT_Done_FreeType(ft);
}
步骤 3:渲染文本
复制代码
void renderText(QOpenGLShaderProgram& program, const std::string& text, GLfloat x, GLfloat y, GLfloat scale) {
    program.bind();
    glActiveTexture(GL_TEXTURE0);
    
    for (auto c = text.begin(); c != text.end(); c++) {
        Character ch = characters[*c];
        GLfloat xpos = x + ch.bearing.x * scale;
        GLfloat ypos = y - (ch.size.y - ch.bearing.y) * scale;
        
        GLfloat w = ch.size.x * scale;
        GLfloat h = ch.size.y * scale;
        
        // 更新 VBO 数据(需预先创建)
        GLfloat vertices = {
            {xpos, ypos + h, 0.0, 0.0},
            {xpos, ypos, 0.0, 1.0},
            {xpos + w, ypos, 1.0, 1.0},
            {xpos, ypos + h, 0.0, 0.0},
            {xpos + w, ypos, 1.0, 1.0},
            {xpos + w, ypos + h, 1.0, 0.0}
        };
        
        glBindTexture(GL_TEXTURE_2D, ch.textureID);
        glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        
        x += (ch.advance >> 6) * scale; // 单位转换为像素
    }
    program.release();
}
关键问题解决
  1. ‌文本模糊‌:

    • 确保纹理过滤设置为 GL_LINEAR。
    • 使用高分辨率字体或 MSDF(多通道有符号距离场)技术。
  2. ‌中文支持‌:

    • FreeType 方法需加载中文字体(如 .ttf),并遍历 Unicode 字符集。
  3. ‌性能优化‌:

    • 批处理文本绘制调用,减少状态切换。
    • 使用实例化渲染(Instancing)处理大量相同字体的文本。
相关推荐
扶尔魔ocy3 天前
【qt openGL入门笔记】windows编译MinGW版本的osgearth(2.10.1)
qt·opengl·osg
扶尔魔ocy4 天前
【QT OpenGL】知识点编目及传送门
qt·opengl·osg
扶尔魔ocy17 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行并进行态势标绘(四)
qt·opengl·osg
:mnong18 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
扶尔魔ocy19 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy20 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy21 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy24 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy25 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl
扶尔魔ocy1 个月前
【qt openGL入门笔记】基础光照
qt·opengl