文本绘制

  本文主要射击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. ACM-ICPC2018徐州网络赛 BE, GE or NE(对抗搜索+博弈+记忆化)

    BE, GE or NE 23.58% 1000ms 262144K   In a world where ordinary people cannot reach, a boy named &quo ...

  2. 百度地图API图标、文本、图例与连线

    百度地图开放平台功能强大,使用简单,为地图的自定义提供了非常方便的途径! 本文以绘制一张全国机器辐射图为例记录其基本使用方法,效果如下图: 图中包括了带图标和文本的标注,连线以及图例. 1.关于坐标 ...

  3. 【读后感1】SQL2008技术内幕- SQL逻辑查询处理

    引言观点 1. 编程语言日新月异,但是从没有人否定sql 在现代编程中的巨大作用和 持续的可利用性.SQL以对人类友好的阅读体验提供数据查询能力( 相比其他编程语言 ), 同时在各种数据库平台中,基础 ...

  4. 无监督学习:Deep Auto-encoder(深度自动编码器)

    一 Auto-encoder NN Encoder & NN Decoder 要一起训练. 二 Starting from PCA 三 Deep Auto-encoder PCA&De ...

  5. 51nod1113【矩阵快速幂】

    思路: 裸的矩阵快速幂,读完题,感觉有点对不起四级算法题这一类. #include<bits/stdc++.h> using namespace std; typedef long lon ...

  6. css 实现垂直水平居中常用方法

    html <div class="outer"> <div class="inner"></div> </div> ...

  7. uoj#401. 【CTSC2018】青蕈领主(分治FFT)

    传送门 话说分治\(FFT\)是个啥子啊--还有题目里那字好像念(蕈xùn) 首先考虑无解的情况:区间相交或者\(L_n\neq n\) 这两个都可以感性理解一下 所以区间之间只会有包含关系,我们把每 ...

  8. python 之 匿名函数

    5.14 匿名函数 lambda x , y : x+y 1 匿名的目的就是要没有名字,给匿名函数赋给一个名字是没有意义的 2 匿名函数的参数规则.作用域关系与有名函数是一样的 3 匿名函数的函数体通 ...

  9. MyBatis二级缓存配置

    正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 Mybatis二级缓存是SessionFactory,如果两次查询基于同一个SessionFactory,那么就从二级缓存 ...

  10. 笔记-JavaWeb学习之旅17

    1.过滤选择器 首元素选择器:first 获得选择的元素中的第一个元素 尾元素选择器:last获得选择元素中的最后一个元素 非元素选择器:not(selector) 不包括指定内容的元素 偶数选择器: ...