或在3D环境校官字符纹理渲染到2D四边形上,绘制更多的图样

基础篇

第一个OpenGL ES项目
制图三角形
什么是Shader?
绘制更多的图样
转换矩阵
透视投影和正交投影
摄像机
绘制一个正方体
基本光照
着力纹理

本子记录

版本号 时间
V1.0 2018.01.20

进阶篇

透明和混合
激光特效
VBO和VAO
顶点索引
制图圆柱体
绘图地形
加载OBJ文件
高级光照
法线贴图
渲染到纹理

前言

OpenGL
图形库项目中平素也没用过,方今也想学着使用这么些图形库,感觉仍旧很有意思,也就自然想着好好的统计一下,希望对我们能拥有协理。下边内容出自迎接来到OpenGL的社会风气
1. OpenGL 图形库使用(一) ——
概念基础

2. OpenGL 图形库使用(二) ——
渲染模式、对象、增添和状态机

3. OpenGL 图形库使用(三) ——
着色器、数据类型与输入输出

4. OpenGL 图形库使用(四) ——
Uniform及更多属性

5. OpenGL 图形库使用(五) ——
纹理

6. OpenGL 图形库使用(六) ——
变换

7. OpenGL 图形库的运用(七)——
坐标种类之五种不同的坐标序列(一)

8. OpenGL 图形库的应用(八)——
坐标体系之3D效果(二)

9. OpenGL 图形库的施用(九)——
视频机(一)

10. OpenGL 图形库的行使(十)——
视频机(二)

11. OpenGL 图形库的使用(十一)——
光照之颜色

12. OpenGL 图形库的利用(十二)——
光照之基础光照

13. OpenGL 图形库的施用(十三)——
光照之材质

14. OpenGL 图形库的行使(十四)——
光照之光照贴图

15. OpenGL 图形库的使用(十五)——
光照之投光物

16. OpenGL 图形库的利用(十六)——
光照之多光源

17. OpenGL 图形库的施用(十七)——
光照之复习总括

18. OpenGL 图形库的行使(十八)——
模型加载之Assimp

19. OpenGL 图形库的使用(十九)——
模型加载之网格

20. OpenGL 图形库的利用(二十)——
模型加载之模型

21. OpenGL 图形库的施用(二十一)——
高级OpenGL之深度测试

22. OpenGL 图形库的行使(二十二)—— 高级OpenGL之模板测试Stencil
testing

23. OpenGL 图形库的使用(二十三)——
高级OpenGL之混合Blending

24. OpenGL 图形库的利用(二十四)—— 高级OpenGL之面剔除Face
culling

25. OpenGL 图形库的运用(二十五)——
高级OpenGL之帧缓冲Framebuffers

26. OpenGL 图形库的拔取(二十六)——
高级OpenGL之立方爱戴图Cubemaps

27. OpenGL 图形库的应用(二十七)—— 高级OpenGL之高级数据Advanced
Data

28. OpenGL 图形库的施用(二十八)—— 高级OpenGL之高级GLSL Advanced
GLSL

29. OpenGL 图形库的行使(二十九)—— 高级OpenGL之几何着色器Geometry
Shader

30. OpenGL 图形库的应用(三十)——
高级OpenGL之实例化Instancing

31. OpenGL 图形库的接纳(三十一)—— 高级OpenGL之抗锯齿Anti
Aliasing

32. OpenGL 图形库的运用(三十二)—— 高级光照之高级光照Advanced
Lighting

33. OpenGL 图形库的选用(三十三)—— 高级光照之Gamma校正Gamma
Correction

34. OpenGL 图形库的利用(三十四)—— 高级光照之阴影 – 阴影映射Shadow
Mapping

35. OpenGL 图形库的运用(三十五)—— 高级光照之阴影 – 点阴影Point
Shadows

36. OpenGL 图形库的选取(三十六)—— 高级光照之法线贴图Normal
Mapping

37. OpenGL 图形库的利用(三十七)—— 高级光照之视差贴图Parallax
Mapping

38. OpenGL 图形库的施用(三十八)——
高级光照之HDR

39. OpenGL 图形库的行使(三十九)——
高级光照之泛光

40. OpenGL 图形库的应用(四十)—— 高级光照之延迟着色法Deferred
Shading

41. OpenGL 图形库的接纳(四十一)——
高级光照之SSAO

42. OpenGL 图形库的运用(四十二)——
PBR之辩护Theory

43. OpenGL 图形库的选拔(四十三)——
PBR之光照Lighting

44. OpenGL 图形库的应用(四十四)——
PBR之几篇没有翻译的英文原稿

45. OpenGL 图形库的施用(四十五)——
实战之调试Debugging

高级篇

纹理投影效果
ShadowMap(一)
ShadowMap(二)
基于CubeMap的反光效率
教你造一面镜子
教您兑现SkyBox
教你打造迷雾
物理引擎
Billboards
粒子效果

文本渲染

当你在图纸总结领域冒险到了自然等级之后你可能会想采取OpenGL来绘制文本。但是,可能与你想象的并不相同,使用像OpenGL这样的底层库来把公文渲染到屏幕上并不是一件简单的工作。假使您只需要绘制128种不同的字符(Character),那么事情也许会简单一些。可是只要您要绘制的字符有着不同的宽、高和边距,事情及时就千头万绪了。依照你使用语言的不比,你或许会需要多于128个字符。再者,假设你要绘制音乐符、数学符号这一个独特的记号;或者渲染竖排文本呢?一旦你把文件这么些扑朔迷离的情事考虑进去,你就不会意外为啥OpenGL这样的最底层API没有包含文本处理了。

由于OpenGL本身并不曾包含其他的文本处理能力,大家不可以不自己定义一套全新的系统让OpenGL绘制文本到屏幕上。由于文本字符没有图元,大家不可能不要有点创制力才行。需要利用的局部技艺可以是:通过GL_LINES来绘制字形,成立文本的3D网格(Mesh),或在3D环境中校字符纹理渲染到2D四边形上。

开发者最常用的一种办法是将字符纹理绘制到四边形上。绘制这一个纹理四边形本身其实并不是很复杂,然则检索要绘制文本的纹路却成为了一项有挑衅性的办事。本课程将探索多种文书渲染的贯彻格局,并且动用FreeType库实现一个越来越高档但更灵敏的渲染文本技术。


经典文本渲染:位图字体

中期的时候,渲染文本是经过甄选一个需要的书体(Font)(或者自己创设一个),并提取这个字体中兼有相关的字符,将它们放到一个单独的大纹理中来贯彻的。那样一张纹理叫做位图字体(Bitmap Font),它在纹理的预定义区域中包含了大家想要使用的保有字符。字体的这多少个字符被喻为字形(Glyph)。每个字形都关系着一个一定的纹理坐标区域。当你想要渲染一个字符的时候,你只需要经过渲染这一块特定的位图字体区域到2D四边形上即可。

公海赌船网址 1

您可以见到,大家取一张位图字体,(通过缜密挑选纹理坐标)从纹理中采样对应的字形,并渲染它们到两个2D四边形上,最后渲染出“OpenGL”文本。通过启用混合,让背景保持透明,最终就能渲染一个字符串到屏幕上。这一个位图字体是由此Codehead的位图字体生成器生成的。

应用那种办法绘制文本有成百上千优势也有为数不少瑕疵。首先,它相对来说很容易实现,并且因为位图字体已经预光栅化了,它的效用也很高。不过,这种方法不够灵活。当你想要使用不同的字体时,你需要再行编译一套全新的位图字体,而且你的顺序会被限制在一个一定的分辨率。假如你对这多少个文件举办缩放的话你会看到文本的像素边缘。此外,这种办法一般会局限于这一个小的字符集,如若您想让它来协助Extended或者Unicode字符的话就很不现实了。

这种绘制文本的办法已经得益于它的疾速和可移植性而异常流行,不过现在早就冒出更加灵活的主意了。其中一个是大家将要探究的运用FreeType库来加载TrueType字体的措施。


现代文件渲染:FreeType

FreeType是一个力所能及用于加载字体并将她们渲染到位图以及提供多种字体相关的操作的软件开发库。它是一个丰硕受欢迎的跨平台字体库,它被用来Mac
OS
X、Java、PlayStation主机、Linux、Android等平台。FreeType的的确吸引力在于它亦可加载TrueType字体。

TrueType字体不是用像素或任何不可缩放的法子来定义的,它是因而数学公式(曲线的重组)来定义的。类似于矢量图像,这多少个光栅化后的字体图像可以依照需要的书体低度来扭转。通过拔取TrueType字体,你可以随意渲染不同尺寸的字形而不造成任何质料损失。

FreeType可以在她们的官方网站中下载到。你可以挑选自己用源码编译这多少个库,如果补助你的阳台的话,你也足以行使他们预编译好的库。请确认你将freetype.lib增长到你项目标链接库中,并且认同编译器知道头文件的地方。

然后请确认含有合适的头文件:

#include <ft2build.h>
#include FT_FREETYPE_H  

是因为FreeType的开发形式(至少在自我写这篇随笔的时候),你无法将它们的头文件放到一个新的目录下。它们应该保留在你include目录的根目录下。通过利用像
#include <FreeType/ft2build.h>如此这般的不二法门导入FreeType可能会油不过生局部头文件争持的题目。

FreeType所做的事就是加载TrueType字体并为每一个字形生成位图以及总计多少个度量值(Metric)。我们得以领取出它生成的位图作为字形的纹路,并运用这多少个度量值定位字符的字形。

要加载一个字体,大家只需要开头化FreeType库,并且将这么些字体加载为一个FreeType称之为面(Face)的事物。这里为大家加载一个从Windows/Fonts目录中拷贝来的TrueType字体文件arial.ttf

FT_Library ft;
if (FT_Init_FreeType(&ft))
    std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;

FT_Face face;
if (FT_New_Face(ft, "fonts/arial.ttf", 0, &face))
    std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;

这个FreeType函数在产出谬误时将回到一个非零的整数值。

光天化日加载成功将来,大家需要定义字体大小,这意味着我们要从字端庄中生成多大的字形:

FT_Set_Pixel_Sizes(face, 0, 48);  

此函数设置了字得体的宽度和低度,将大幅度值设为0表示大家要从字体面通过给定的惊人中动态总计出字形的幅度。

一个FreeType面中隐含了一个字形的聚众。我们得以调用FT_Load_Char函数来将内部一个字形设置为激活字形。那里大家挑选加载字符字形’X’:

if (FT_Load_Char(face, 'X', FT_LOAD_RENDER))
    std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;

通过将FT_LOAD_RENDER设为加载标记之一,大家报告FreeType去创造一个8位的灰度位图,大家可以经过face->glyph->bitmap来做客这一个位图。

利用FreeType加载的每个字形没有一样的轻重(不像位图字体这样)。使用FreeType生成的位图的大小刚好能包含那些字符可见区域。例如生成用于表示’.’的位图的分寸要比表示’X’的小得多。由此,FreeType同样也加载了有些心胸值来指定每个字符的深浅和职务。下边这张图展现了FreeType对每一个字符字形总括的享有度量值。

公海赌船网址 2

每一个字形都置身一个程度的基准线(Baseline)上(即上图中水平箭头提醒的这条线)。一些字形恰好位于基准线上(如’X’),而另一些则会有些越过基准线以下(如’g’或’p’)(译注:即那个包含下伸部的字母,可以见这里)。这多少个度量值精确定义了摆设字形所需的每个字形距离基准线的偏移量,每个字形的轻重缓急,以及需要预留多少空间来渲染下一个字形。下边那些表列出了大家需要的拥有属性。

公海赌船网址 3

在急需渲染字符时,我们得以加载一个字符字形,获取它的度量值,并扭转一个纹理,但每一帧都这么做会要命没有效能。我们应将这多少个生成的多寡储存在程序的某一个地点,在急需渲染字符的时候再去调用。我们会定义一个不行有利的结构体,并将这么些结构体存储在一个map中。

struct Character {
    GLuint     TextureID;  // 字形纹理的ID
    glm::ivec2 Size;       // 字形大小
    glm::ivec2 Bearing;    // 从基准线到字形左部/顶部的偏移值
    GLuint     Advance;    // 原点距下一个字形原点的距离
};

std::map<GLchar, Character> Characters;

对此这多少个科目来说,本着让漫天简单的目标,大家只生成ASCII字符集的前128个字符。对每一个字符,大家转变一个纹理并保留有关数据至Character结构体中,之后再添加至Characters那多少个映射表中。这规范,渲染一个字符所需的拥有数据就都被贮存下来备用了。

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //禁用字节对齐限制
for (GLubyte c = 0; c < 128; c++)
{
    // 加载字符的字形 
    if (FT_Load_Char(face, c, FT_LOAD_RENDER))
    {
        std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
        continue;
    }
    // 生成纹理
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(
        GL_TEXTURE_2D,
        0,
        GL_RED,
        face->glyph->bitmap.width,
        face->glyph->bitmap.rows,
        0,
        GL_RED,
        GL_UNSIGNED_BYTE,
        face->glyph->bitmap.buffer
    );
    // 设置纹理选项
    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);
    // 储存字符供之后使用
    Character character = {
        texture, 
        glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
        glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
        face->glyph->advance.x
    };
    Characters.insert(std::pair<GLchar, Character>(c, character));
}

在这一个for循环中咱们遍历了ASCII集中的方方面面128个字符,并拿到它们对应的字符字形。对每一个字符,我们转移了一个纹理,设置了它的选项,并蕴藏了它的度量值。有趣的是大家这里将纹理的internalFormatformat设置为GL_RED。通过字形生成的位图是一个8位灰度图,它的每一个颜料都由一个字节来表示。由此我们需要将位图缓冲的每一字节都作为纹理的颜色值。那是通过创办一个相当的纹理实现的,这多少个纹理的每一字节都对应着纹理颜色的庚寅革命分量(颜色向量的首先个字节)。假诺我们运用一个字节来表示纹理的水彩,我们需要留意OpenGL的一个限制:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);   

OpenGL要求具备的纹路都是4字节对齐的,即纹理的大小永远是4字节的倍数。日常这并不会产出什么问题,因为多数纹理的幅度都为4的倍数并/或每像素使用4个字节,然则现在我们各个像素只用了一个字节,它能够是即兴的增幅。通过将纹精通压对齐参数设为1,这样才能担保不会有对齐问题(它或许会招致段错误)。

当你处理完字形后并非遗忘清理FreeType的资源。

FT_Done_Face(face);
FT_Done_FreeType(ft);

公海赌船网址,1. 着色器

咱俩将利用上边的终极着色器来渲染字形:

#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.0, 1.0);
    TexCoords = vertex.zw;
}

俺们将地方和纹理纹理坐标的数据合起来存在一个vec4中。这几个极限着色器将地点坐标与一个投影矩阵相乘,并将纹理坐标传递给一些着色器:

#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).r);
    color = vec4(textColor, 1.0) * sampled;
}

一部分着色器有几个uniform变量:一个是单颜色通道的字形位图纹理,另一个是颜色uniform,它可以用来调整文本的结尾颜色。我们第一从位图纹理中采样颜色值,由于纹理数据中仅存储着革命分量,我们就采样纹理的r重量来作为取样的alpha值。通过转移颜色的alpha值,最终的水彩在字形背景颜色上会是晶莹剔透的,而在真的的字符像素上是不透明的。大家也将RGB颜色与textColor这一个uniform相乘,来更换文本颜色。

本来我们需要启用混合才能让这整个使得:

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  

对于投影矩阵,大家将动用一个正射投影矩阵(Orthographic Projection Matrix)。对于文本渲染我们(平时)都不需要透视,使用正射投影同样允许大家在屏幕坐标系中设定所有的顶峰坐标,假设大家采纳如下格局部署:

glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 600.0f);

俺们设置投影矩阵的平底参数为0.0f,并将顶部参数设置为窗口的高度。这样做的结果是大家指定了y坐标的界定为屏幕底边(0.0f)至屏幕顶部(600.0f)。这表示现在点(0.0,
0.0)对应左下角(译注:而不再是窗口正中间)。

终极要做的事是创建一个VBOVAO用来渲染四边形。现在咱们在开首化VBO时分配充裕的内存,这样我们得以在渲染字符的时候再来更新VBO的内存。

GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
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);      

各样2D四边形需要6个终端,每个终端又是由一个4float向量(译注:一个纹理坐标和一个巅峰坐标)组成,因而大家将VBO的内存分配为6
*
4个float的高低。由于我们会在绘制字符时平时更新VBO的内存,所以我们将内存类型设置为GL_DYNAMIC_DRAW

2. 渲染一行文本

要渲染一个字符,我们从后边创设的Characters映射表中取出对应的Character结构体,并基于字符的度量值来测算四边形的维度。依照四边形的维度大家就能动态总结出6个描述四边形的巅峰,并使用glBufferSubData函数更新VBO所管理内存的情节。

咱俩创造一个誉为RenderText的函数渲染一个字符串:

void RenderText(Shader &s, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color)
{
    // 激活对应的渲染状态
    s.Use();
    glUniform3f(glGetUniformLocation(s.Program, "textColor"), color.x, color.y, color.z);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(VAO);

    // 遍历文本中所有的字符
    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 w = ch.Size.x * scale;
        GLfloat h = ch.Size.y * scale;
        // 对每个字符更新VBO
        GLfloat vertices[6][4] = {
            { xpos,     ypos + h,   0.0, 0.0 },            
            { xpos,     ypos,       0.0, 1.0 },
            { xpos + w, ypos,       1.0, 1.0 },

            { xpos,     ypos + h,   0.0, 0.0 },
            { xpos + w, ypos,       1.0, 1.0 },
            { xpos + w, ypos + h,   1.0, 0.0 }           
        };
        // 在四边形上绘制字形纹理
        glBindTexture(GL_TEXTURE_2D, ch.textureID);
        // 更新VBO内存的内容
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); 
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        // 绘制四边形
        glDrawArrays(GL_TRIANGLES, 0, 6);
        // 更新位置到下一个字形的原点,注意单位是1/64像素
        x += (ch.Advance >> 6) * scale; // 位偏移6个单位来获取单位为像素的值 (2^6 = 64)
    }
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

本条函数的情节应该十分强烈了:大家率先总括出四边形的原点坐标(为xposypos)和它的大小(为w和h),并生成6个极端形成这多少个2D四边形;注意我们将每个度量值都使用scale举行缩放。接下来我们改进了VBO的情节、并渲染了那多少个四边形。

其中这行代码需要倍加注意:

GLfloat ypos = y - (ch.Size.y - ch.Bearing.y);   

一对字符(如’p’或’q’)需要被渲染到基准线以下,由此字形四边形也应当被摆放在RenderText的y值以下。ypos的偏移量可以从字形的度量值中查获:

公海赌船网址 4

要总结这段距离,即偏移量,我们需要找出字形在基准线之下延展出去的距离。在上图中这段距离用革命箭头标出。从度量值中得以看出,我们得以通过用字形的惊人减去bearingY来计量这段向量的尺寸。对于那么些刚刚位于基准线上的字符(如’X’),这一个值正好是0.0。而对于这多少个超越基准线的字符(如’g’或’j’),这些值则是正的。

倘诺您每件事都做对了,那么你现在已经足以采取下边的话语成功渲染字符串了:

RenderText(shader, "This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3, 0.7f, 0.9f));

渲染效果看上去像这么:

公海赌船网址 5

你可以从这里获取这么些事例的源代码。

// Std. Includes
#include <iostream>
#include <map>
#include <string>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// FreeType
#include <ft2build.h>
#include FT_FREETYPE_H
// GL includes
#include "Shader.h"

// Properties
const GLuint WIDTH = 800, HEIGHT = 600;

/// Holds all state information relevant to a character as loaded using FreeType
struct Character {
    GLuint TextureID;   // ID handle of the glyph texture
    glm::ivec2 Size;    // Size of glyph
    glm::ivec2 Bearing;  // Offset from baseline to left/top of glyph
    GLuint Advance;    // Horizontal offset to advance to next glyph
};

std::map<GLchar, Character> Characters;
GLuint VAO, VBO;

void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color);

// The MAIN function, from here we start our application and run the Game loop
int main()
{
    // Init GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed
    glfwMakeContextCurrent(window);

    // Initialize GLEW to setup the OpenGL Function pointers
    glewExperimental = GL_TRUE;
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // Set OpenGL options
    glEnable(GL_CULL_FACE);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Compile and setup the shader
    Shader shader("shaders/text.vs", "shaders/text.frag");
    glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(WIDTH), 0.0f, static_cast<GLfloat>(HEIGHT));
    shader.Use();
    glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));

    // FreeType
    FT_Library ft;
    // All functions return a value different than 0 whenever an error occurred
    if (FT_Init_FreeType(&ft))
        std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;

    // Load font as face
    FT_Face face;
    if (FT_New_Face(ft, "fonts/arial.ttf", 0, &face))
        std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;

    // Set size to load glyphs as
    FT_Set_Pixel_Sizes(face, 0, 48);

    // Disable byte-alignment restriction
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 

    // Load first 128 characters of ASCII set
    for (GLubyte c = 0; c < 128; c++)
    {
        // Load character glyph 
        if (FT_Load_Char(face, c, FT_LOAD_RENDER))
        {
            std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
            continue;
        }
        // Generate texture
        GLuint texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(
            GL_TEXTURE_2D,
            0,
            GL_RED,
            face->glyph->bitmap.width,
            face->glyph->bitmap.rows,
            0,
            GL_RED,
            GL_UNSIGNED_BYTE,
            face->glyph->bitmap.buffer
        );
        // Set texture options
        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);
        // Now store character for later use
        Character character = {
            texture,
            glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
            glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
            face->glyph->advance.x
        };
        Characters.insert(std::pair<GLchar, Character>(c, character));
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    // Destroy FreeType once we're finished
    FT_Done_Face(face);
    FT_Done_FreeType(ft);


    // Configure VAO/VBO for texture quads
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    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);

    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check and call events
        glfwPollEvents();

        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        RenderText(shader, "This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
        RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3, 0.7f, 0.9f));

        // Swap the buffers
        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color)
{
    // Activate corresponding render state  
    shader.Use();
    glUniform3f(glGetUniformLocation(shader.Program, "textColor"), color.x, color.y, color.z);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(VAO);

    // Iterate through all characters
    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 w = ch.Size.x * scale;
        GLfloat h = ch.Size.y * scale;
        // Update VBO for each character
        GLfloat vertices[6][4] = {
            { xpos,     ypos + h,   0.0, 0.0 },            
            { xpos,     ypos,       0.0, 1.0 },
            { xpos + w, ypos,       1.0, 1.0 },

            { xpos,     ypos + h,   0.0, 0.0 },
            { xpos + w, ypos,       1.0, 1.0 },
            { xpos + w, ypos + h,   1.0, 0.0 }           
        };
        // Render glyph texture over quad
        glBindTexture(GL_TEXTURE_2D, ch.TextureID);
        // Update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        // Render quad
        glDrawArrays(GL_TRIANGLES, 0, 6);
        // Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
    }
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

为了让你更好通晓我们是怎么总结四边形顶点的,咱们可以关闭混合来探望真正渲染出来的四边形是怎么样体统的:

公海赌船网址 6

可以看来,大部分的四边形都位于一条(想象中的)基准线上,而对应’p’或’(‘字形的四边形则有些向下偏移了一部分。


更进一步

本学科演示了怎么利用FreeType库绘制TrueType文本。这种措施灵活、可缩放并协助多种字符编码。然则,由于我们对每一个字形都转移并渲染了纹路,你的应用程序可能并不需要这么强大的功用。性能更好的位图字体也许是更可取的,因为对持有的字形我们只需要一个纹理。当然,最好的方法是整合这二种模式,动态变化包含所有字符字形的位图字体纹理,并用FreeType加载。这为渲染器节省了大量纹理切换的开支,并且按照字形的排列紧密程度也足以省去成千上万的属性开销。

另一个行使FreeType字体的题目是字形纹理是储存为一个稳住的字体大小的,因而一向对其拓宽就会现出锯齿边缘。此外,对字形举行旋转还会使它们看上去变得模糊。这几个题材可以经过储存每个像素距近年来的字形概略的离开,而不是光栅化的像素颜色,来解决。这项技能被誉为有向距离场(Signed Distance Fields),Valve在几年前刊登过一篇了论文,研究了她们经过这项技艺来拿到异常棒的3D渲染效果。

后记

本篇已截止,前面更非凡~~~~

公海赌船网址 7

相关文章