经典文字渲染:位图字体

在早期渲染文字时,选择你应用程序的字体(或者创建你自己的字体)来绘制文字是通过将所有用到的文字加载在一张大纹理图中来实现的。这张纹理贴图我们把它叫做位图字体(Bitmap Font),它包含了所有我们想要使用的字符。这些字符被称为字形(Glyph)。每个字形根据他们的编号被放到位图字体中的确切位置,在渲染这些字形的时候根据这些排列规则将他们取出并贴到指定的2D方块中。

上图展示了我们如何从一张位图字体的纹理中通过对字形的合理取样(通过小心地选择字形的纹理坐标)来实现绘制文字“OpenGL”到2D方块中的原理。通过对OpenGL启用混合并让位图字体的纹理背景保持透明,这样就能实现使用OpenGL绘制你想要文字到屏幕的功能。上图的这张位图字体是使用Codehead的位图字体生成器生成的。

使用这种途径来绘制文字有许多优势也有很多缺点。首先,它相对来说很容易实现,并且因为位图字体被预渲染好,使得这种方法效率很高。然而,这种途径并不够灵活。当你想要使用不同的字体时,你不得不重新生成位图字体,以及你的程序会被限制在一个固定的分辨率:如果你对这些文字进行放大的话你会看到文字的像素边缘。此外,这种方式仅局限于用来绘制很少的字符,如果你想让它来扩展支持Unicode文字的话就很不现实了。

这种绘制文字的方式曾经得益于它的高速和可移植性而非常流行,然而现在已经存在更加灵活的方式了。其中一个是我们即将展开讨论的使用FreeType库来加载TrueType字体的方式。

现代文字渲染:FreeType

FreeType是一个能够用于加载字体并将他们渲染到位图以及提供多种字体相关的操作的软件开发库。它是一个非常受欢迎的跨平台字体库,被用于 Mac OSX、Java、PlayStation主机、Linux、Android等。FreeType的真正吸引力在于它能够加载TrueType字体。

TrueType字体不采用像素或其他不可缩放的方式来定义,而是一些通过数学公式(曲线的组合)。这些字形,类似于矢量图像,可以根据你需要的字体大小来生成像素图像。通过使用TrueType字体可以轻易呈现不同大小的字符符号并且没有任何质量损失。

由于框架用的就是SDL,所以这里用的是SDL_TTF2.0,实际上SDL_TTF是把FreeType进行了一次封装。

定义一个用来储存这些属性的结构体,并创建一个字符表来存储这些字形属性。

struct Character {GLuint     TextureID;Vector2       Size;Vector2        Bearing;GLuint      Advance;    //Offset to advance to next glyph
};

加载的思路就是预先生成表示128个ASCII字符的字符表。并为每一个字符储存纹理和一些度量值。这样,所有需要的字符就被存下来备用了。

//Init characterTTF_Init();font = TTF_OpenFont("../../Assets/consola.ttf", 24);SDL_Color color = { 255, 0, 0 };SDL_Surface *face = NULL;//14 is a magic number...for (GLubyte c = 14; c < 128; c++){char tmpChar = /*'a' +*/ c;string tmp(1,tmpChar);face = TTF_RenderText_Blended(font, tmp.c_str(), color);int mode;if (face->format->BytesPerPixel == 3) { // RGB 24bitmode = GL_RGB;}else if (face->format->BytesPerPixel == 4) { // RGBA 32bitmode = GL_RGBA;}else {SDL_FreeSurface(face);return false;}GLuint texture = 0;glGenTextures(1, &texture);glBindTexture(GL_TEXTURE_2D, texture);glTexImage2D(GL_TEXTURE_2D, 0, mode, face->w, face->h, 0, mode, GL_UNSIGNED_BYTE, face->pixels);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);int minx, maxx, miny, maxy, advance;if (TTF_GlyphMetrics(font, *(tmp.c_str()), &minx, &maxx, &miny, &maxy, &advance) == -1){printf("%s\n", TTF_GetError());}Character character = {texture,Vector2(face->w, face->h),Vector2(minx, maxy),advance};Characters.insert(std::pair<GLchar, Character>(tmpChar, character));}characterShader = new ShaderProgram("../../Assets/shader/character.vert", "../../Assets/shader/character.frag");Matrix4x4 projection = Transform::OrthoFrustum(0.0f, static_cast<GLfloat>(creationFlags.width), 0.0f, static_cast<GLfloat>(creationFlags.height), -1000, 1000);characterShader->Use();glUniformMatrix4fv(glGetUniformLocation(characterShader->GetProgramID(), "projection"), 1, GL_FALSE, &projection[0]);// Configure VAO/VBO for texture quadsglGenVertexArrays(1, &characterVAO);glGenBuffers(1, &characterVBO);glBindVertexArray(characterVAO);glBindBuffer(GL_ARRAY_BUFFER, characterVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);return true;

注意这里专门开了正交透视矩阵来进行字体渲染

Matrix4x4 projection = Transform::OrthoFrustum(0.0f, static_cast<GLfloat>(creationFlags.width), 0.0f, static_cast<GLfloat>(creationFlags.height), -1000, 1000);
characterShader->Use();
glUniformMatrix4fv(glGetUniformLocation(characterShader->GetProgramID(), "projection"), 1, GL_FALSE, &projection[0]);

关于投影矩阵可以参考这个: 详解MVP矩阵之ProjectionMatrix

关于TTF_GlyphMetrics,可以参考下图

看一下着色器

#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;uniform mat4 projection;void main()
{gl_Position = projection * vec4(vertex.xy, 0, 1.0);TexCoords = vertex.zw;
}
#version 330 core
in vec2 TexCoords;
out vec4 color;uniform sampler2D text;
uniform vec3 textColor;void main()
{    vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).a);color = vec4(textColor, 1.0) * sampled;
}

渲染文字的接口实现如下

void Renderer::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, Color color)
{characterShader->Use();glUniform3f(glGetUniformLocation(characterShader->GetProgramID(), "textColor"), color.r, color.g, color.b);//glUniform3f(glGetUniformLocation(s.Program, "textColor"), color.r, color.g, color.b);glActiveTexture(GL_TEXTURE0);glBindVertexArray(characterVAO);std::string::const_iterator c;for (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 ypos = y;GLfloat w = ch.Size.x * scale;GLfloat h = ch.Size.y * scale;// VBOGLfloat vertices[6][4] = {{ xpos,     ypos,   0.0, 1.0 },{ xpos,     ypos + h,       0.0, 0.0 },{ xpos + w, ypos + h,       1.0, 0.0 },{ xpos,     ypos,   0.0, 1.0 },{ xpos + w, ypos + h,       1.0, 0.0 },{ xpos + w, ypos,   1.0, 1.0 }};// Render glyph texture over quadglBindTexture(GL_TEXTURE_2D, ch.TextureID);// Update content of VBO memoryglBindBuffer(GL_ARRAY_BUFFER, characterVBO);glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferDataglBindBuffer(GL_ARRAY_BUFFER, 0);// Render quadglDrawArrays(GL_TRIANGLES, 0, 6);// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)x += ch.Advance  * scale; }glBindVertexArray(0);glBindTexture(GL_TEXTURE_2D, 0);
}

现在如果要在屏幕上显示FPS,只需要

RenderText(fps.str(), - 0.5f * parent->GetWindowWidth(), 0.5f * parent->GetWindowHeight() - 100, 1, Color(1, 1, 1, 1));

结果

参考

Learnopengl_文字渲染

SDLTutorial Series - Part 6 - Displaying Text with SDL_ttf

TTF_GlyphMetrics

OpenGL进阶(二十一) - 文字渲染相关推荐

  1. OpenGL基础55:文字渲染

    一.FreeType库 FreeType是一个能够提供多种字体相关的操作的软件开发库,往往使用它来做最简单的文字渲染: OpenGL环境配置(超全整合版)FreeType库可以从这篇文章中的链接中下载 ...

  2. 【OpenGL】二十一、OpenGL 矩阵压栈与出栈 ( 不同类型矩阵变换先后顺序 | 渲染前不设置单位阵 | 压栈出栈原理分析 | 代码示例 )

    文章目录 一.不同类型矩阵变换先后顺序 二.渲染前不设置单位阵 三.矩阵的压栈和出栈原理分析 四.矩阵的压栈和出栈代码示例 五.相关资源 一.不同类型矩阵变换先后顺序 对 OpenGL 中的 模型视图 ...

  3. Java进阶(二十一)java 空字符串与null区别

    java 空字符串与null区别 1.类型 null表示的是一个对象的值,而并不是一个字符串.例如声明一个对象的引用,String a = null ; ""表示的是一个空字符串, ...

  4. OpenGL文字渲染

    一.简介 由于OpenGL本身并没有定义如何渲染文字到屏幕,也没有用于表示文字的基本图形,我们必须自己定义一套全新的方式才能让OpenGL来绘制文字.目前一些技术包括:通过GL_LINES来绘制字形. ...

  5. Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学 ...

  6. OpenGL text rendering文字渲染的实例

    OpenGL text rendering文字渲染 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <iostream> #inclu ...

  7. 【SQL开发实战技巧】系列(二十一):数据仓库中时间类型操作(进阶)识别重叠的日期范围,按指定10分钟时间间隔汇总数据

    系列文章目录 [SQL开发实战技巧]系列(一):关于SQL不得不说的那些事 [SQL开发实战技巧]系列(二):简单单表查询 [SQL开发实战技巧]系列(三):SQL排序的那些事 [SQL开发实战技巧] ...

  8. 高质量实时渲染课程笔记(二)——图形学基础回顾(渲染管线、OpenGL入门、GLSL、渲染方程)

    文章目录 1 图形渲染管线 2 OpenGL 2.1 使用OpenGL过程的比喻: 油画过程 2.2 Place objects/models 放这些模型 模型这么摆放 2.3 Set up an e ...

  9. JavaScript 编程精解 中文第三版 二十一、项目:技能分享网站

    二十一.项目:技能分享网站 原文:Project: Skill-Sharing Website 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了<JavaScri ...

最新文章

  1. UI设计师面试时如何介绍自己?
  2. redis源码分析(beta版本)-redis实现的概述逻辑
  3. python 笔记 之 sqlalchemy操作数据库-创建表
  4. java hashmap 初始化赋值_HashMap引用传递,对象直接赋值,修改的是同一个对象,new HashMap「」(Map)才是生成新的对象...
  5. docker查询mysql 有哪些版本的镜像_CentOS安装Docker环境和mysql镜像的记录
  6. 并行计算教程简介 Introduction to Parallel Computing Tutorial
  7. API接口应该如何设计?
  8. [专栏精选]Unity刚体详解
  9. 如何调整cmd窗口的颜色
  10. CCF推荐国际学术会议与学术期刊
  11. Spring Batch 入门教程
  12. DirectX修复工具(DirectX Repair)是一款系统级工具软件,简便易用。本程序为绿色版,无需安装,可直接运行。 本程序的主要功能是检测当前系统的DirectX状态,如果发现异常
  13. win10系统迁移到固态硬盘ssd
  14. JavaScript---网络编程(8)-DHTML技术演示(1)
  15. QQ另存为出现“你没有权限在此位置中保存文件,请与管理员联系以获得相应权限”
  16. html中scr作用,可控硅的作用是什么
  17. 【JS】两个等号和三个等号的区别
  18. 一笔画问题(nyoj 42)
  19. 区块链 : 历史、现在与未来
  20. Linux 驱动USB键盘驱动入门demo

热门文章

  1. php 定时热舞,Rythm.js – 用javascript让你的页面跳舞
  2. 雷达 航迹跟踪 matlab,基于MATLAB的雷达与AIS航迹融合处理
  3. 基于springboot垃圾分类网站设计实现【毕业论文、源码】
  4. Machine Learning with Python Cookbook 学习笔记 第9章
  5. ABAP_ALV_Function方式与OO方式(较为简单、普通的ALV)
  6. 【洛谷P1359租用游艇】
  7. 真的来了!首批科创板IPO受理企业名单出炉
  8. 征途2无法显示服务器列表,Win10上玩征途2提示DriverCommlnit驱动加载失败是怎么回事...
  9. 机械师T58-V加装机械硬盘
  10. 重构SeleniumeDownloader底层浏览器驱动