当前位置: 首页 > article >正文

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)处理大量相同字体的文本。

http://www.kler.cn/a/611518.html

相关文章:

  • Vue的实例
  • Three.js 快速入门教程【十八】射线拾取模型——鼠标点击屏幕选中模型或物体
  • 【AI大模型】搭建本地大模型GPT-J:详细步骤及常见问题
  • 计算机视觉中的椭圆带权平均算法全解析
  • 【NLP 44、实践 ⑪ 用Bert模型结构实现自回归语言模型的训练】
  • Docker技术系列文章,第七篇——Docker 在 CI/CD 中的应用
  • 全息教学系统的软件开发,沉浸式数字沙盘展示系统如何改变历史教学
  • 孟德尔随机化:脑卒中研究新钥匙
  • Linux 设备分类详解:字符设备、块设备与网络设备解析
  • Java后端API限流秘籍:高并发的防护伞与实战指南
  • MySQL 5.7升级8.0报异常:ONLY_FULL_GROUP_BY
  • 飞速(FS)InfiniBand解决方案助力领先科技公司网络升级
  • playwright-go实战:自动化登录测试
  • 华为Pura先锋盛典及鸿蒙智家产品发布,空气算法重构健康家居“阔”美学
  • Linux的一些常见指令
  • 探索PyMOL新插件NRGSuite-Qt:全面提升分子对接、结合位点预测与动力学模拟的研究效率
  • 低空智能目标(无人机)管理控制系统技术详解
  • 进军场景智能体,云迹机器人又快了一步
  • 【C++网络编程】第8篇:协议设计与序列化(Protobuf、FlatBuffers)
  • Containerd+Kubernetes搭建k8s集群