文本绘制

  本文主要射击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. java之装箱拆箱

    参考http://how2j.cn/k/number-string/number-string-wrap/22.html 封装类 所有的基本类型,都有对应的类类型 比如int对应的类是Integer ...

  2. Linear Algebra - Determinant(基础)

    1. 行列式的定义 一阶行列式: \[ \begin{vmatrix} a_1 \end{vmatrix} = a_1 \] 二阶行列式: \[ \begin{vmatrix} a_{11} & ...

  3. JAVA企业级开发-xml基础语法&约束&解析(04)

    一.什么是xml html:超文本标记语言.它主要是用来封装页面上要显示的数据,最后通过浏览器来解析html文件,然后把数据展示在浏览器上.同样我们可以使用JS和DOM技术对html文件进行解析和操作 ...

  4. Docker环境下的前后端分离项目部署与运维(七)Redis高速缓存

    Redis高速缓存 利用内存保存数据,读写速度远超硬盘:可以减少I/O操作,降低I/O压力. 发红包.抢红包的数据可以存在高速缓存中,加快处理速度,不需要经过数据库 淘宝首页一些优惠活动商品等热数据可 ...

  5. JSP 标准标签库(JSTL)(菜鸟教程)

    菜鸟教程 JSTL 1.1 与 JSTL 1.2 之间的区别?如何下载 JSTL 1.2? JSTL 1.2 中不要求 standard.jar 包. 您可以在 Maven 中央仓库中找到它们. ht ...

  6. [sdut] 1400 马的走法 dfs

    Problem Description 在一个4*5的棋盘上,马的初始位置坐标(纵 横)位置由键盘输入,求马能返回初始位置的所有不同走法的总数(马走过的位置不能重复,马走“日”字).如果马的初始位置坐 ...

  7. ai技术体系

  8. Unity 自动寻路Navmesh之跳跃,攀爬,斜坡

    在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节.然而,如果要做一个场景精美的手游,需要用到各种复杂的场景地形,而不仅仅是平地上的自动寻路.今天我们将通过一个完整的 ...

  9. 洛谷P1655 小朋友的球(Stirling数)

    P1655 小朋友的球 题目描述 @发源于 小朋友最近特别喜欢球.有一天他脑子抽了,从口袋里拿出了N个不同的球,想把它们放到M个相同的盒子里,并且要求每个盒子中至少要有一个球,他好奇有几种放法,于是尝 ...

  10. JDK12下的ArrayList源码解读 与 Vector的对比

    ArrayList源码阅读. //测试代码实现如下 private static void arrayList() { ArrayList<String> list = new Array ...