文本绘制

  本文主要射击Freetype的入门理解和在OpenGL中实现文字的渲染。

freetype

  freetype的官网,本文大部分内容参考https://www.freetype.org/freetype2/docs/tutorial/step1.html#section-2

library

  FreeType中的library其类型是FT_Library,定义如下:

typedef struct FT_LibraryRec_  *FT_Library;

  所以可以简单的理解为一个FT_LibraryRec_的对象,虽然FreeType用c写的,但这个地方不妨碍我们使用对象来理解它。因为_LibraryRec_我并没有看到源代码,具体包含哪些内容我并不清楚。但根据其用法,可以推测其应该是一些字体上下文的内容,比如缓存、内存管理等。

FT_Error = FT_Init_Freetype(&library);

face

  一个face可以理解为字体的描述或者说字形的集合,比如“Times New Roman Regular"表示正常的新罗马字体,而"Times New Roman Italic"表示新罗马字体的斜体表示。一个字体文件中可以嵌入多个face,我们可以通过下面的API加载特定的face:

  

FT_Error FT_New_Face(FT_Library library,const char* filepathname,FT_Long face_index,FT_Face *aface);

  其中face_index就表示要加载字体文件中的哪个face,一般情况下,0总是可用的。如果face_index设置为-1,则可以通过face->num_faces获取字体文件中有多少个face。

error = FT_New_Face(library,"./arial.ttf",,&face);

字体大小

error = FT_Set_Char_Size(
face, /* handle to face object */
, /* char_width in 1/64th of points */
*, /* char_height in 1/64th of points */
, /* horizontal device resolution */
); /* vertical device resolution */

  在这里有两个概念需要注意:pt和dpi。pt是point的缩写,可以立即为一个点,一个点的大小使用物理距离来描述的,通常是1/72英寸。dpi的意思是dot per inch,表示每英寸有多少个点,这个点表示的是显示设备最小输出单元,可以理解为我们常说的像素,主流显示设备的标准值是72dpi和96dpi,这个可以在显示器配置中查到。所以在这个方法中通过同时指定pt大小和设备的dpi,根据换算关系可以计算字符的像素大小。

  下面的代码直接设置字形的像素大小。注意这两个方法中,width的值设置为0,表示width与height一样,height设置为0同理。

error = FT_Set_Pixel_Sizes(
face, /* handle to face object */
, /* pixel_width */
); /* pixel_height */

glyph

  字形,其实就是某个字符具体的形状描述。字形的描述有两种,一种是位图,一种是矢量图。位图又叫点阵图,简单理解就是图像是由一个一个像素点组成,每个像素点可以有自己的颜色;矢量图则记录的是一个一个的对象,这些对象是一种形状,形状由数学公式来描述的,简单的理解就是矢量图记录的是图像的画法。这两种描述最大的区别就是,缩放的时候矢量图不会失真。

  使用FreeType的时候,我们不需要关心这些底层的实现,直接使用FT_Load_Glyph即可加载。

FT_UInt FT_Get_Char_Index( FT_Face   face,
FT_ULong charcode ); FT_Error FT_Load_Glyph( FT_Face face,
FT_UInt glyph_index, //The index of the glyph in the font file.
FT_Int32 load_flags ); //A flag indicating what to load for this glyph
FT_Error FT_Render_Glyph( face->glyph,   /* glyph slot  */
render_mode ); /* render mode */
 

  字形的数据结构:

 typedef struct  FT_GlyphSlotRec_
{
FT_Library library;
FT_Face face;
FT_GlyphSlot next;
FT_UInt reserved; /* retained for binary compatibility */
FT_Generic generic; FT_Glyph_Metrics metrics;
FT_Fixed linearHoriAdvance;
FT_Fixed linearVertAdvance;
FT_Vector advance; FT_Glyph_Format format; FT_Bitmap bitmap;
FT_Int bitmap_left;
FT_Int bitmap_top; FT_Outline outline; FT_UInt num_subglyphs;
FT_SubGlyph subglyphs; void* control_data;
long control_len; FT_Pos lsb_delta;
FT_Pos rsb_delta; void* other; FT_Slot_Internal internal; } FT_GlyphSlotRec;

  在字形数据结构中,几个比较重要的数据是bitmap,bitmap_left,bitmap_top。其中bitmap其实可以理解为字形的位图数据,定义如下:

  typedef struct  FT_Bitmap_
{
unsigned int rows;
unsigned int width;
int pitch;
unsigned char* buffer;
unsigned short num_grays;
unsigned char pixel_mode;
unsigned char palette_mode;
void* palette; } FT_Bitmap;

  其中buffer像素数据,rows,width表示图像的宽和高,我们在OpenGL中绘制时主要用到的三个数据。

  除了bitmap,还有几个重要的数据,这写数据主要影响多个文本之间的布局,因为不是每个字形的大小都是一样的,比如bitmap_left,bitmap_right等。让我们以下图为例说明一下FreeType对于字形的描述:

  每一个字形都放在一个水平的基准线(Baseline)上(即上图中水平箭头指示的那条线)。一些字形恰好位于基准线上(如’X’),而另一些则会稍微越过基准线以下(如’g’或’p’)(译注:即这些带有下伸部的字母,可以见这里)。这些度量值精确定义了摆放字形所需的每个字形距离基准线的偏移量,每个字形的大小,以及需要预留多少空间来渲染下一个字形。下面这个表列出了我们需要的所有属性。

在OpenGL中绘制文字 

  FreeType 官网有些例子给我们参考,点击这里查看。其中一个简单的例子是在终端中以字符的形式输出字形,这个例子可以帮助我们理解bitmap中的数据形式。

  获取到字形的bitmap后,在OpenGL绘制文字的思路就很简单了:根据字形数据生成二维纹理,然后把这个纹理绘制到一个四方形中,以下是绘制的主要代码,完整代码见:https://github.com/xin-lover/opengl-learn/tree/master/chapter-16-glfw/chapter-font

  glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //禁用字节对齐限制
unsigned int textureID;
glGenTextures(,&textureID); glBindTexture(GL_TEXTURE_2D,textureID);
glTexImage2D(GL_TEXTURE_2D,,format,width,height,,format,GL_UNSIGNED_BYTE,data);
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);

 在这里需要注意字节对齐的问题,OpenGL默认要求纹理4字节对齐,即纹理的大小是4的倍数,这通常不会有什么问题,因为绝大多数的纹理大小都是4的倍数并/或每个橡树4字节大小,但现在我们一个像素是一个字节(GL_RED),它可以是任意的宽度,所以需要需求这个限制,将对齐参数设置为1,不然的话可能会造成段错误。

glPixelStorei(GL_UNPACK_ALIGNMENT, );//禁用字节对齐限制

效果:

demo参考网址:https://learnopengl-cn.github.io/06%20In%20Practice/02%20Text%20Rendering/

Linux OpenGL 实践篇-16 文本绘制的更多相关文章

  1. Linux OpenGL 实践篇-9 模型

    之前一直渲染箱子,显得有点单调.这一次我们绘制一个用艺术家事先用建模工具创建的模型. 本次实践参考:https://learnopengl-cn.github.io/03%20Model%20Load ...

  2. Linux OpenGL 实践篇-3 绘制三角形

    本次实践是绘制两个三角形,重点理解顶点数组对象和OpenGL缓存的使用. 顶点数组对象 顶点数组对象负责管理一组顶点属性,顶点属性包括位置.法线.纹理坐标等. OpenGL缓存 OpenGL缓存实质上 ...

  3. Linux OpenGL 实践篇-5 纹理

    纹理 在之前的实践中,我们所渲染的物体的表面颜色都是纯色或者根据顶点位置计算出的一个颜色,这种方式在表现物体细节方面是比较吃资源的,因为我们每增加一个细节,我们就需要定义更多的顶点及其属性.所以美术人 ...

  4. Linux OpenGL 实践篇-4 坐标系统

    OpenGL中顶点经过顶点着色器后会变为标准设备坐标系.标准设备坐标系的各坐标的取值范围是[-1,1],超过这个范围的点将会被剔除.而这个变换的过程可描述为顶点在几个坐标系统的变换,这几个坐标系统为: ...

  5. Linux OpenGL 实践篇-2 创建一个窗口

    OpenGL 作为一个图形接口,并没有包含窗口的相关内容,但OpenGL使用必须依赖窗口,即必须在窗口中绘制.这就要求我们必须了解一种窗口系统,但不同的操作系统提供的创建窗口的API都不相同,如果我们 ...

  6. Linux OpenGL 实践篇-14-多实例渲染

    多实例渲染 OpenGL的多实例渲染是一种连续执行多条相同的渲染命令的方法,并且每条命令产生的结果都有轻微的差异,通常用于渲染大量的几何物体. 设想一个场景,比如太空,我们需要渲染数以万记的星球,如果 ...

  7. Linux OpenGL 实践篇-13-geometryshader

    几何着色器 几何着色器是位于图元装配和片元着色器之前的一个着色器阶段,是一个可选阶段.它的输入是一个图元的完整的顶点信息,通常来自于顶点着色器,但如果细分计算着色器启用的话,那输入则是细分计算着色器的 ...

  8. Linux OpenGL 实践篇-11-shadow

    OpenGL 阴影 在三维场景中,为了使场景看起来更加的真实,通常需要为其添加阴影,OpenGL可以使用很多种技术实现阴影,其中有一种非常经典的实现是使用一种叫阴影贴图的实现,在本节中我们将使用阴影贴 ...

  9. Linux OpenGL 实践篇-10-framebuffer

    在之前的实践中我们都是在当前的窗口中渲染,即使用的缓存都是由glutCreateWindow时创建的缓存,我们可称之为默认缓存.它是唯一一个可以被图形服务器的显示系统识别的帧缓存,我们在屏幕上看到的只 ...

随机推荐

  1. 为一个vector<cv::KeyPoint*> 类型的变量做初始化

    vector<cv::KeyPoint*> keypoints; int N; keypoints = vector<cv::KeyPoint*>(N, static_cast ...

  2. 在MongoDB中修改数据类型

    引言 本文主要讲解Mongodb的类型转换.包括:string转double, string转int, string转Date. 0. 出现类型不一致的原因 ES导入数据到Mongo后,会出现类型统一 ...

  3. 基于Go实现的秒杀系统

    这是基于Go语言的一个秒杀系统,这个系统分三层,接入层.逻辑层.管理层.项目源码:https://github.com/BlueSimle/SecKill 系统架构图 秒杀接入层 从Etcd中加载秒杀 ...

  4. 数据库路由中间件MyCat - 源代码篇(17)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...

  5. 程序员除了会CRUD之外,还应该知道什么叫CQRS!

    今天主要跟大家分享一下什么是 CQRS,以及在项目中如何去使用.   CRUD系统 我们平常最熟悉的就是三层架构,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的实体.然后通过业 ...

  6. es备份索引

    1.解压https://github.com/medcl/esm-abandonedhttps://github.com/medcl/esm-abandoned/releases tar xf lin ...

  7. 优酷土豆的Redis服务平台化之路

    前言 Nginx 是一个 免费的 , 开源的 , 高性能 的 HTTP 服务器和 反向代理 ,以及 IMAP / POP3代理服务器. Nginx 以其高性能,稳定性,丰富的功能,简单的配置和低资源消 ...

  8. PAT甲级——1111 Online Map (单源最短路经的Dijkstra算法、priority_queue的使用)

    本文章同步发布在CSDN:https://blog.csdn.net/weixin_44385565/article/details/90041078   1111 Online Map (30 分) ...

  9. Luogu P2341 [HAOI2006]受欢迎的牛 SCC缩点

    把强连通分量缩点,如果有且仅有一个出度为0的强连通分量,那么答案就是他的size:如果有多个入度为0的,那么没有明星牛. #include<cstdio> #include<iost ...

  10. Codeforces 27D(二分染色)

    要点 将边作为染色,如果交叉则异色 #include <cstdio> #include <algorithm> #include <functional> usi ...