Linux OpenGL 实践篇-16 文本绘制
文本绘制
本文主要射击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 文本绘制的更多相关文章
- Linux OpenGL 实践篇-9 模型
之前一直渲染箱子,显得有点单调.这一次我们绘制一个用艺术家事先用建模工具创建的模型. 本次实践参考:https://learnopengl-cn.github.io/03%20Model%20Load ...
- Linux OpenGL 实践篇-3 绘制三角形
本次实践是绘制两个三角形,重点理解顶点数组对象和OpenGL缓存的使用. 顶点数组对象 顶点数组对象负责管理一组顶点属性,顶点属性包括位置.法线.纹理坐标等. OpenGL缓存 OpenGL缓存实质上 ...
- Linux OpenGL 实践篇-5 纹理
纹理 在之前的实践中,我们所渲染的物体的表面颜色都是纯色或者根据顶点位置计算出的一个颜色,这种方式在表现物体细节方面是比较吃资源的,因为我们每增加一个细节,我们就需要定义更多的顶点及其属性.所以美术人 ...
- Linux OpenGL 实践篇-4 坐标系统
OpenGL中顶点经过顶点着色器后会变为标准设备坐标系.标准设备坐标系的各坐标的取值范围是[-1,1],超过这个范围的点将会被剔除.而这个变换的过程可描述为顶点在几个坐标系统的变换,这几个坐标系统为: ...
- Linux OpenGL 实践篇-2 创建一个窗口
OpenGL 作为一个图形接口,并没有包含窗口的相关内容,但OpenGL使用必须依赖窗口,即必须在窗口中绘制.这就要求我们必须了解一种窗口系统,但不同的操作系统提供的创建窗口的API都不相同,如果我们 ...
- Linux OpenGL 实践篇-14-多实例渲染
多实例渲染 OpenGL的多实例渲染是一种连续执行多条相同的渲染命令的方法,并且每条命令产生的结果都有轻微的差异,通常用于渲染大量的几何物体. 设想一个场景,比如太空,我们需要渲染数以万记的星球,如果 ...
- Linux OpenGL 实践篇-13-geometryshader
几何着色器 几何着色器是位于图元装配和片元着色器之前的一个着色器阶段,是一个可选阶段.它的输入是一个图元的完整的顶点信息,通常来自于顶点着色器,但如果细分计算着色器启用的话,那输入则是细分计算着色器的 ...
- Linux OpenGL 实践篇-11-shadow
OpenGL 阴影 在三维场景中,为了使场景看起来更加的真实,通常需要为其添加阴影,OpenGL可以使用很多种技术实现阴影,其中有一种非常经典的实现是使用一种叫阴影贴图的实现,在本节中我们将使用阴影贴 ...
- Linux OpenGL 实践篇-10-framebuffer
在之前的实践中我们都是在当前的窗口中渲染,即使用的缓存都是由glutCreateWindow时创建的缓存,我们可称之为默认缓存.它是唯一一个可以被图形服务器的显示系统识别的帧缓存,我们在屏幕上看到的只 ...
随机推荐
- 一些unity资源
雨凇解包 http://www.xuanyusong.com/archives/3618 http://www.cnblogs.com/lixiang-share/p/5840444.html u3d ...
- JS 上拉加载
$(document).ready( function(){ $contentLoadTriggered = false; $("#mainDiv").scroll(functio ...
- 你必须学写 Python 装饰器的五个理由
你必须学写Python装饰器的五个理由 ----装饰器能对你所写的代码产生极大的正面作用 作者:Aaron Maxwell,2016年5月5日 Python装饰器是很容易使用的.任何一个会写Pytho ...
- CentOS6.7 i686上安装JDK7
内核版本: [root@heima01 java]# uname -a Linux heima01 2.6.32-573.el6.i686 #1 SMP Thu Jul 23 12:37:35 UTC ...
- JSON解析器之jackson json数据和java对象转换
- ELK系列(1) - Elasticsearch + Logstash + Kibana + Log4j2快速入门与搭建用例
前言 最近公司分了个ELK相关的任务给我,在一边学习一边工作之余,总结下这些天来的学习历程和踩坑记录. 首先介绍下使用ELK的项目背景:在项目的数据库里有个表用来存储消息队列的消费日志,这些日志用于开 ...
- STP-11-多生成树:IEEE 802.1s
IEEE802.1s多生成树(MultipleSpanningTrees,MST)有时也称为多STP(MultipleSTP,MSTP),它定义了在使用802.1QVLAN网络中,部署多实例STP的标 ...
- C# dynamic与var的区别
1.C#编程总结(十四)dynamic 2.var和dynamic的区别及如何正确使用dynamic?
- 2019湘潭校赛 G(并查集)
要点 题目传送 题目本质是每个点必属于两个集合中的一个,伴随的性质是:如果一个人说别人true,则他们一定属于同一阵营:如果说别人fake,一定不属于同一阵营. 每个点拆为\(i\)和\(i + n\ ...
- Webservice入门简单实例
转载大神 项目目的: 程序A调用程序B中的方法C.. https://blog.csdn.net/lovebosom/article/details/51558139 ...