[C++] [OpenGL] 用FreeType渲染文字

参考

文本渲染 - LearnOpenGL CN
Why shouldn’t I use global variables?

介绍

OpenGL并不自带文本渲染,需要自己去得到文本的位图然后渲染出来。
以前用的是位图字体,分辨率有限,放大会有锯齿。
现在有了更好的替代方案:矢量图,相比于位图,矢量图用数学定义(赞美数学),就不会有锯齿了。
差不多就是这样。
FreeType官网
(p.s.字体是我直接从C:\Windows\Fonts里拷贝过来的)
(p.s.全局变量听起来确实是一个糟糕的设计,所以没用。详见参考的第二个链接。)

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include FT_FREETYPE_H

struct UserData
{
	GLuint program;
	GLuint vao;
	std::map<char, GLuint> textures;
	char currentTexture;

	FT_Library lib;
	FT_Face face;
};

void check(std::string str)
{
	std::cout << str << ":0x" << std::hex << glGetError() << std::dec << std::endl;
}

void display()
{
	UserData* ud = ((std::shared_ptr<UserData>*)glutGetWindowData())->get();

	glClear(GL_COLOR_BUFFER_BIT);

	glUseProgram(ud->program);
	check("use program");
	glBindVertexArray(ud->vao);
	check("bind vao");
	glBindTexture(GL_TEXTURE_2D, ud->textures.at(ud->currentTexture));
	check("bind texture");
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	check("draw arrays");
	glBindVertexArray(0);

	glutSwapBuffers();
}

void keyboard(unsigned char key, int, int)
{
	UserData* ud = ((std::shared_ptr<UserData>*)glutGetWindowData())->get();

	if (key == 27)
	{
		exit(EXIT_SUCCESS);
	}
	else if (key >= 0 && key < 128)
	{
		ud->currentTexture = key;
		glutPostRedisplay();
	}
}

std::string loadString(std::string filename)
{
	std::ifstream fin;
	fin.open(filename);
	std::stringstream sstr;
	std::string temp;
	while (getline(fin, temp))
	{
		sstr << temp << std::endl;
	}
	fin.close();
	return sstr.str();
}

GLuint createProgram(std::string vfilename, std::string ffilename)
{
	int success;
	char infoLog[1024];

	std::string vsource = loadString(vfilename);
	std::string fsource = loadString(ffilename);
	const char* vsource_ref = vsource.data();
	const char* fsource_ref = fsource.data();
	GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
	GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(vshader, 1, &vsource_ref, nullptr);
	glShaderSource(fshader, 1, &fsource_ref, nullptr);
	glCompileShader(vshader);
	glGetShaderiv(vshader, GL_COMPILE_STATUS, &success);
	std::cout << "vshader GL_COMPILE_STATUS:" << success << std::endl;
	glGetShaderInfoLog(vshader, 1024, nullptr, infoLog);
	std::cout << "vshader info log:" << infoLog << std::endl;
	glCompileShader(fshader);
	glGetShaderiv(fshader, GL_COMPILE_STATUS, &success);
	std::cout << "fshader GL_COMPILE_STATUS:" << success << std::endl;
	glGetShaderInfoLog(fshader, 1024, nullptr, infoLog);
	std::cout << "fshader info log:" << infoLog << std::endl;

	GLuint program = glCreateProgram();
	glAttachShader(program, vshader);
	glAttachShader(program, fshader);
	glLinkProgram(program);
	glGetProgramiv(program, GL_LINK_STATUS, &success);
	std::cout << "program GL_LINK_STATUS:" << success << std::endl;
	glGetProgramInfoLog(program, 1024, nullptr, infoLog);
	std::cout << "program info log:" << infoLog << std::endl;

	glDeleteShader(vshader);
	glDeleteShader(fshader);

	return program;
}

GLuint createVAO()
{
	float ver[] =
	{
		-1, 1,
		    -1, -1,
		    1, -1,
		    1, 1
	    };
	float tex[] =
	{
		0, 0,
		0, 1,
		1, 1,
		1, 0
	};

	GLuint vao;
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);
	GLuint vbo[2];
	glGenBuffers(2, vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(ver), ver, GL_STATIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 2, GL_FLOAT, false, 2 * sizeof(float), nullptr);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(tex), tex, GL_STATIC_DRAW);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, false, 2 * sizeof(float), nullptr);
	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glDeleteBuffers(2, vbo);

	return vao;
}

GLuint createTexture(char ch)
{
	UserData* ud = ((std::shared_ptr<UserData>*)glutGetWindowData())->get();
	std::cout << "load char:" << FT_Load_Char(ud->face, ch, FT_LOAD_RENDER) << std::endl;

	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, ud->face->glyph->bitmap.width, ud->face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, ud->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_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	return texture;
}

int main(int argc, char* argv[])
{
	std::shared_ptr<UserData> ud = std::make_shared<UserData>();

	glutInit(&argc, argv);
	glutInitContextFlags(GLUT_FORWARD_COMPATIBLE | GLUT_DEBUG);
	glutInitContextProfile(GLUT_CORE_PROFILE);
	glutInitContextVersion(3, 3);

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutCreateWindow("");
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutSetWindowData(&ud);

	std::cout << "glewInit() == GLEW_OK:" << (glewInit() == GLEW_OK) << std::endl;
	ud->program = createProgram("resources/text.vert", "resources/text.frag");
	check("create program");
	ud->vao = createVAO();
	check("create vao");

	std::cout << "init freetype:" << FT_Init_FreeType(&ud->lib) << std::endl;
	std::cout << "new face:" << !FT_New_Face(ud->lib, "resources/arial.ttf", 0, &ud->face) << std::endl;
	FT_Set_Pixel_Sizes(ud->face, 0, 100);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 
	for (unsigned char i = 0; i < 128; i++)
	{
		ud->textures.insert({i, createTexture(i)});
	}
	glBindTexture(GL_TEXTURE_2D, 0);
	FT_Done_Face(ud->face);
	FT_Done_FreeType(ud->lib);
	
	ud->currentTexture = 'a';

	glutMainLoop();

	glDeleteProgram(ud->program);
	glDeleteVertexArrays(1, &ud->vao);
	for (unsigned char i = 0; i < 128; i++)
	{
		glDeleteTextures(1, &ud->textures.at(i));
	}

	return 0;
}

text.vert

#version 330 core

layout(location=0)in vec2 a_position;
layout(location=1)in vec2 a_texCoord;

out vec2 v_texCoord;

void main()
{
	gl_Position = vec4(a_position,0.0,1.0);
	v_texCoord = a_texCoord;
}

text.frag

#version 330 core

in vec2 v_texCoord;
out vec4 o_color;

uniform sampler2D u_sampler;

void main()
{
	float r = texture(u_sampler,v_texCoord).r;
	o_color = r * vec4(1.0,1.0,1.0,1.0);
}

运行

[C++] [OpenGL] 用FreeType渲染文字_第1张图片
[C++] [OpenGL] 用FreeType渲染文字_第2张图片
[C++] [OpenGL] 用FreeType渲染文字_第3张图片

你可能感兴趣的:(OpenGL/OpenGL,ES/EGL,c++,opengl,freetype)