一: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();
}
關鍵問題解決
-
?文本模糊?:
- 確保紋理過濾設置為?
GL_LINEAR
。 - 使用高分辨率字體或 MSDF(多通道有符號距離場)技術。
- 確保紋理過濾設置為?
-
?中文支持?:
- FreeType 方法需加載中文字體(如?
.ttf
),并遍歷 Unicode 字符集。
- FreeType 方法需加載中文字體(如?
-
?性能優化?:
- 批處理文本繪制調用,減少狀態切換。
- 使用實例化渲染(Instancing)處理大量相同字體的文本。