首页 > 学院 > 开发设计 > 正文

OpenGL 绘制简单的英文字符

2019-11-08 03:03:21
字体:
来源:转载
供稿:网友

http://learnopengl-cn.readthedocs.io/zh/latest/06%20In%20PRactice/02%20Text%20Rendering/

FreeType 配置 下载FreeType2 得到: ft271.zip 解压 freetype-2.7.1 在CMake中,如下图 顺序 这里写图片描述

编译成功后得到 lib 文件 freetype271.lib 可自行编译成不同版本 (在此为 LIB Release)

这里写图片描述

再用VS 新建一个工程,把 /freetype-2.7.1/include 目录包含进工程, 并将Lib文件复至工程下 输入如下代码,运行成功, 即 freetype271.lib 使用正常

#include <ft2build.h> #include FT_FREETYPE_H #include <iostream> 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:"<<face->num_glyphs<<endl; cout<<"num_faces:"<<face->num_faces<<endl; system("Pause"); return 0; } OpenGL

主程序:

#include <iostream>#include <map>#include <string>#define GLEW_STATIC#include <GL/glew.h>// GLFW#include <GLFW/glfw3.h>// FreeType#include <ft2build.h>#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<GLchar, Character> 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<GLfloat>(WIDTH), 0.0f, static_cast<GLfloat>(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<GLchar, Character>(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 <string>#include <fstream>#include <sstream>#include <iostream>#include <gl/glew.h>#include <string>#include <fstream>#include <sstream>#include <iostream>#include <GL/glew.h>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 <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>#include <glm/gtc/type_ptr.hpp>void Shader::useShaderPrograme(){ glUseProgram(this->m_nProgram); // 使用porgram}#endifShader部分 GLSL

text.vs

#version 330 corelayout (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;}

text.frag

#version 330 corein 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


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表