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);// 解绑VAOglBindVertexArray(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_Hstruct 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.mzph.cn/diannao/75689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零构建大语言模型全栈开发指南:第二部分:模型架构设计与实现-2.1.3前馈网络(FFN)与激活函数(GELU)优化

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 2.1.3 前馈网络(FFN)与激活函数(GELU)优化1. 前馈网络(FFN)的架构设计与数学原理1.1 FFN在Transformer中的核心作用2. GELU激活函数的数学特性与优化2.1 GELU的数学形式与近似计算3. 逐行代码实现…

React 中的错误边界(Error Boundaries),如何使用它们捕获组件错误

大白话React 中的错误边界&#xff08;Error Boundaries&#xff09;&#xff0c;如何使用它们捕获组件错误 在 React 里&#xff0c;错误边界就像是一个“小卫士”&#xff0c;专门负责在组件出现错误时挺身而出&#xff0c;避免整个应用因为一个小错误就崩溃掉。接下来我会详…

数据库DBA认证,选哪个认证合适?

从 Oracle、MySQL 到 云数据库&#xff0c;结合市场认可度、考试难度及职业回报&#xff0c;为你精选高性价比认证。 一、企业级数据库认证&#xff08;传统场景&#xff09; 1. Oracle认证 认证等级考试代码核心内容费用适合人群OCA1Z0-082SQL基础、数据库安装与配置$245零基…

力扣刷题-热题100题-第24题(c++、python)

234. 回文链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/palindrome-linked-list/description/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 数组是连续的存储空间&#xff0c;可以根据索引到达任意位置&#xff0c;链表只能一个个的顺…

调用通义千问实现语音合成并将合成的音频通过扬声器播放

1. 作者介绍 郭建东&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2024级研究生 研究方向&#xff1a;机器视觉与人工智能 电子邮件&#xff1a;1229963266qq.com 高金年&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2024级研究生&…

Ubuntu软件包离线下载安装

1、下载软件包tcpd&#xff0c;并在/var/cache/apt/archives目录中查看。 rooteducoder:~# apt-get install -d tcpd Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed:tcpd …

您的数据是如何出现在暗网上的?

暗网是互联网上的一个隐秘角落&#xff0c;人们可以在那里保持匿名。暗网经常与深网混淆&#xff0c;但它们并不完全相同。 深网是指网络上所有未被搜索引擎索引的内容。这包括电子邮件帐户、私人数据库和付费服务等。这并不违法&#xff0c;只是无法通过简单的 Google 搜索找…

原型模式及其应用

引言 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它允许通过复制现有对象来创建新对象&#xff0c;而无需通过构造函数来创建。这种模式通过克隆现有对象来创建新对象&#xff0c;从而避免了复杂的初始化过程。本文将探讨原型模式的好…

thinkphp漏洞再现

Thinkphp5x远程命令执行及getshell 1、开环境 2、使用工具攻击 开启工具 输入地址&#xff0c;点击漏洞检测 存在漏洞之后&#xff0c;选择漏洞&#xff0c;执行命令 3、也可以执行远程命令 执行命令 ?sindex/think\app/invokefunction&functioncall_user_func_array&…

Day16 -实例:Web利用邮箱被动绕过CDN拿真实ip

本想测试一下全局ping&#xff0c;刚好注册的时候收到了邮件&#xff0c;刚好去做一下复现。 原理&#xff1a;主动让对方站点给我们发邮件&#xff08;注册、修改密码、订阅推送等&#xff09;我们查看邮件原文&#xff0c;原文里存在真实的邮件站点ip 特点&#xff1a;邮件…

vue3 数据监听(watch、watchEffect)

1、watch 1.1基本使用 作用&#xff1a;数据监听 语法&#xff1a; watch(监听的数据, (改变后的数据, 改变前的数据) > { console.log(newVal, oldVal); }) 注意点&#xff1a;watch写法上支持一个或者多个监听源&#xff0c;这些监听源必须只能是getter/effect函数…

网盘解析工具更新,解决了一些bug

解析工具v1.2.1版本更新&#xff0c;本次是小版本更新&#xff0c;修复了一些bug。 之前小伙伴反应的网盘进入文件后不能返回上一级&#xff0c;现在这个bug修复了&#xff0c;已经可以点击了。 点击资源后会回到资源那一级目录&#xff0c;操作上是方便了不少。 增加了检查自…

推荐1款简洁、小巧的实用收音机软件,支持手机和电脑

聊一聊 没想到现在还有人喜欢听广播。 我一直以为听广播必须要用那种小广播机才可以。 原来手机或电脑上也是可以的。 今天给大家分享一款可以在电脑和手机上听广播的软件。 软件介绍 龙卷风收音机 电台广播收音机分电脑和手机两个版本。 电脑端无需安装&#xff0c;下载…

六十天前端强化训练之第三十一天之Webpack 基础配置 大师级讲解(接下来几天给大家讲讲工具链与工程化)

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 一、Webpack 核心概念解析 二、实战&#xff1a;多资源打包配置&#xff08;含完整代码&#xff09; 三、配置深度解析&#xff08;重点部分说明&#xff09; 四、效果演示…

机器学习——Bagging、随机森林

相比于Boosting的集成学习框架&#xff0c;Bagging(Bootstrap Sampling&#xff0c;自助聚集法&#xff0c;又称为自助采样)作为一种自助聚集且并行化的集成学习方法&#xff0c;其通过组合多个基学习器的预测结果来提高模型的稳定性和泛化能力。其中随机森林是Bagging学习框架…

【蓝桥杯】每日练习 Day13

前言 今天做了不少题&#xff0c;但是感觉都太水了&#xff0c;深思熟虑之下主播决定拿出两道相对不那么水的题来说一下&#xff08;其实还是很水&#xff09;。 两道问题&#xff0c;一道是日期问题&#xff08;模拟&#xff09;&#xff0c;一道是区间合并问题。 日期差值 …

HTML输出流

HTML 输出流 JavaScript 中**「直接写入 HTML 输出流」**的核心是通过 document.write() 方法向浏览器渲染过程中的数据流动态插入内容。以下是详细解释&#xff1a; 一、HTML 输出流的概念 1. 动态渲染过程 HTML 文档的加载是自上而下逐行解析的。当浏览器遇到 <script&…

理解文字识别:一文读懂OCR商业化产品的算法逻辑

文字识别是一项“历久弥新”的技术。早在上世纪初&#xff0c;工程师们就开始尝试使用当时有限的硬件设备扫描并识别微缩胶片、纸张上的字符。随着时代和技术的发展&#xff0c;人们在日常生活中使用的电子设备不断更新换代&#xff0c;文字识别的需求成为一项必备的技术基础&a…

开源模型应用落地-语音转文本-whisper模型-AIGC应用探索(五)

一、前言 在上一节中&#xff0c;学习了如何使用vLLM来部署Whisper-large-v3-turbo模型。不过&#xff0c;在实际使用时&#xff0c;模型一次只能处理30秒的音频。今天&#xff0c;将结合实际业务&#xff0c;介绍如何处理一段完整的音频&#xff0c;并生成相应的字幕文件。 相…

“十五五”时期航空弹药发展环境分析

1&#xff0e;“十五五”时期航空弹药发展环境分析 &#xff08;标题&#xff1a;小二号宋体居中&#xff09; 一、建言背景介绍 &#xff08;一级标题&#xff1a;黑体三号&#xff0c;首行空两格&#xff09; 航空弹药作为现代战争的核心装备&#xff0c;其发展水平直接关乎…