http://learnopengl-cn.readthedocs.io/zh/latest/06%20In%20Practice/02%20Text%20Rendering/
编译成功后得到 lib 文件 freetype271.lib 可自行编译成不同版本 (在此为 LIB Release)
再用VS 新建一个工程,把 \freetype-2.7.1\include 目录包含进工程, 并将Lib文件复至工程下
输入如下代码,运行成功, 即 freetype271.lib 使用正常
#include
#include FT_FREETYPE_H
#include
using namespace std;
int main()
{
FT_Library library;
FT_Init_FreeType(&library);
FT_Face face;
FT_New_Face(library, "msyh.ttf", 0, &face);
cout<<"num_glyphs:"<num_glyphs<cout<<"num_faces:"<num_faces<"Pause");
return 0;
}
主程序:
#include
#include
#include
#define GLEW_STATIC
#include
// GLFW
#include
// FreeType
#include
#include FT_FREETYPE_H
// GL includes
#include "Shader.h"
#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glew32s.lib")
#pragma comment (lib, "glfw3.lib")
#pragma comment (lib, "freetype271.lib")
const GLuint WIDTH = 400, HEIGHT = 300;
// 保存字体信息
struct Character {
GLuint TextureID; // 字形纹理ID
glm::ivec2 Size; // 字形大大小
glm::ivec2 Bearing; // 字形基于基线和起点的位置
GLuint Advance; // 起点到下一个字形起点的距离
};
GLuint VAO, VBO;
std::map mapCharacters;
void RenderText(Shader &shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color);
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
int main()
{
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* pWnd = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
glfwMakeContextCurrent(pWnd);
glewExperimental = GL_TRUE;
glewInit();
glViewport(0, 0, WIDTH, HEIGHT);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND); // 开启混合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Shacer加载编译
Shader shader("./Shader/text.vs", "./Shader/text.frag");
// 正交投影
glm::mat4 projection = glm::ortho(0.0f, static_cast(WIDTH), 0.0f, static_cast(HEIGHT));
shader.useShaderPrograme();
glUniformMatrix4fv(glGetUniformLocation(shader.getPrograme(), "projection"),
1, GL_FALSE, glm::value_ptr(projection));
// FreeType
FT_Library ft;
if (FT_Init_FreeType(&ft))
std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
// 加载字体
FT_Face tfFace;
if (FT_New_Face(ft, "./abc.ttf", 0, &tfFace))
std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;
FT_Set_Pixel_Sizes(tfFace, 0, 30); // 字体宽高 0宽 则自动处理
// 1字节对齐 即禁用自动对齐方式
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// 只生成表示128个ASCII字符的字符表。并为每一个字符储存纹理和一些度量值
for (GLubyte c = 0; c < 128; c++)
{
//if (FT_Load_Char(tfFace, c, FT_LOAD_RENDER))
//wchar_t chinese_char = '经';
if (FT_Load_Char(tfFace, c, FT_LOAD_RENDER))
{
std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
continue;
}
// 生成纹理
GLuint nTextureID;
glGenTextures(1, &nTextureID);
glBindTexture(GL_TEXTURE_2D, nTextureID);
{
//void glTexImage2D (GLenum target, GLint level, GLint internalformat,
// GLsizei width, GLsizei height,
// GLint border, GLenum format, GLenum type, const void *pixels);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED,
tfFace->glyph->bitmap.width, tfFace->glyph->bitmap.rows,
0, GL_RED, GL_UNSIGNED_BYTE, tfFace->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 = { nTextureID,
glm::ivec2(tfFace->glyph->bitmap.width, tfFace->glyph->bitmap.rows),
glm::ivec2(tfFace->glyph->bitmap_left, tfFace->glyph->bitmap_top),
tfFace->glyph->advance.x };
mapCharacters.insert(std::pair(c, character));
}
glBindTexture(GL_TEXTURE_2D, 0);
} // for
// 释放
FT_Done_Face(tfFace);
FT_Done_FreeType(ft);
// VAO/VBO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
{
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
{
// GL_DyNAMIC_DRAW:表示该缓存区会被周期性更改
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);
// 循环 事件与渲染
while (!glfwWindowShouldClose(pWnd))
{
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
RenderText(shader, "yulinxx copy", 25.0f, 25.0f, 2.0f, glm::vec3(0.5, 0.8f, 0.2f));
RenderText(shader, "(C) LearnOpenGL.com", 40.0f, 270.0f, 1.0f, glm::vec3(0.3, 0.7f, 0.9f));
glfwSwapBuffers(pWnd);
}
glfwTerminate();
return 0;
}
// 渲染文字
void RenderText(Shader &shader, std::string strText, GLfloat x, GLfloat y, GLfloat nScale, glm::vec3 vecColor)
{
shader.useShaderPrograme();
glUniform3f(glGetUniformLocation(shader.getPrograme(), "textColor"), vecColor.x, vecColor.y, vecColor.z);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
{
// 遍历绘制所有文字
std::string::const_iterator c;
for (c = strText.begin(); c != strText.end(); c++)
{
Character ch = mapCharacters[*c];
GLfloat xpos = x + ch.Bearing.x * nScale;
GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * nScale;
GLfloat w = ch.Size.x * nScale;
GLfloat h = ch.Size.y * nScale;
// 每个2D方块需要6个顶点,每个顶点由一个4维向量(一个纹理坐标和一个顶点坐标)组成
// 当前字符的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);
}
glBindTexture(GL_TEXTURE_2D, 0);
// 更新位置到下一个字形的原点,注意单位是1/64像素
x += (ch.Advance >> 6) * nScale; // //(2^6 = 64)
}
}
glBindVertexArray(0);
}
Shader.h
//Shader.h
#pragma once
#ifndef TEXTURE_SHADER_H_
#define TEXTURE_SHADER_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class Shader
{
public:
Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
~Shader();
public:
void useShaderPrograme();
GLuint getPrograme() {
return this->m_nProgram;
}
private:
GLuint m_nProgram;
};
Shader::Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
{
std::string vertexCode;
std::string fragmentCode;
std::ifstream vertexShaderF;
std::ifstream fragementShaderF;
vertexShaderF.exceptions(std::ifstream::badbit);
fragementShaderF.exceptions(std::ifstream::badbit);
try
{
vertexShaderF.open(vertexPath); // 打开文件
fragementShaderF.open(fragmentPath);
std::stringstream vertexShaderStream, fragementShaderStream;
vertexShaderStream << vertexShaderF.rdbuf(); // 读取文件至stringstream中
fragementShaderStream << fragementShaderF.rdbuf();
vertexShaderF.close();
fragementShaderF.close();
vertexCode = vertexShaderStream.str(); // 转换成string类型
fragmentCode = fragementShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ:" << std::endl;
}
const GLchar* pVertexCode = vertexCode.c_str(); // string 转 char*
const GLchar* pFragementCode = fragmentCode.c_str();
GLuint nVertexShader, nFragementShader;
GLint nRes = 0;
GLchar chLogInfo[512] = { '\0' };
// 创建顶点着色器
nVertexShader = glCreateShader(GL_VERTEX_SHADER);
// 将顶点着色程序的源代码字符数组绑定到顶点着色器对象
glShaderSource(nVertexShader, 1, &pVertexCode, nullptr);
glCompileShader(nVertexShader); // compile shader 编译着色器
// 获取编译结果
glGetShaderiv(nVertexShader, GL_COMPILE_STATUS, &nRes);
if (!nRes)
{
glGetShaderInfoLog(nVertexShader, 512, nullptr, chLogInfo);
std::cout << "ERROR::SHADEF::VERTEX::COMPILATION_FAILED:" << chLogInfo << std::endl;
}
// 创建片断着色器
nFragementShader = glCreateShader(GL_FRAGMENT_SHADER);
// 将片段着色程序的源代码字符数组绑定到片段着色器对象
glShaderSource(nFragementShader, 1, &pFragementCode, nullptr);
glCompileShader(nFragementShader);
glGetShaderiv(nFragementShader, GL_COMPILE_STATUS, &nRes);
if (!nRes)
{
glGetShaderInfoLog(nFragementShader, 512, nullptr, chLogInfo);
std::cout << "ERROR::SHADEF::FRAGEMENT::COMPILATION_FAILED:" << chLogInfo << std::endl;
}
this->m_nProgram = glCreateProgram(); // 创建GLSL程序
glAttachShader(this->m_nProgram, nVertexShader); // 绑定shader到program
glAttachShader(this->m_nProgram, nFragementShader);
// glLinkProgram操作产生最后的可执行程序,它包含最后可以在硬件上执行的硬件指令
glLinkProgram(this->m_nProgram); // 链接
glGetProgramiv(this->m_nProgram, GL_LINK_STATUS, &nRes);
if (!nRes)
{
glGetProgramInfoLog(this->m_nProgram, 512, nullptr, chLogInfo);
std::cout << "ERROR::SHADEF::FRAGEMENT::LINK_FAILED:" << chLogInfo << std::endl;
}
glDeleteShader(nVertexShader);
glDeleteShader(nFragementShader);
}
Shader::~Shader()
{
}
#include
#include
#include
void Shader::useShaderPrograme()
{
glUseProgram(this->m_nProgram); // 使用porgram
}
#endif
text.vs
#version 330 core
layout (location = 0) in vec4 vertex; //
out vec2 TexCoords;
uniform mat4 projection;
// 顶点着色器将会将位置坐标与投影矩阵相乘,
// 并将纹理坐标转发给片段着色器:
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
text.frag
#version 330 core
in vec2 TexCoords;
out vec4 color;
// 单颜色通道的字形位图纹理
uniform sampler2D text;
// 文字的颜色
uniform vec3 textColor;
// 纹理数据中仅存储着红色分量,就通过r分量来作为取样颜色的aplpha值。
// 结果值是一个字形背景为纯透明,而字符部分为不透明的白色 的颜色。
// 将此颜色与字体颜色uniform值相乘就得到了要输出的字符颜色了
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}
运行效果:
源码下载: VS2015
http://download.csdn.net/detail/yulinxx/9757228