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

参考

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

介绍

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

代码

#include <iostream>
#include <sstream>
#include <fstream>
#include <memory>
#include <map>
#include <gl/glew.h>
#include <gl/freeglut.h>
#include <ft2build.h>
#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);
}

运行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

要在OpenGL中输出文字,您需要使用一个库或API,例如: 1. FreeType库 - 它是一个免费的,开源的库,用于处理字体文件,并将字形转换为可呈现的形式。 2. GLFW库 - 它是一个用于创建窗口和处理用户输入的库,它还提供了一个简单的API用于输出文字。 下面是使用FreeType库在OpenGL中输出文字的步骤: 1. 下载并安装FreeType库。 2. 创建一个OpenGL窗口。 3. 加载字体文件并创建字形。 4. 将字形转换为纹理。 5. 在OpenGL窗口中呈现纹理。 下面是代码示例: ``` #include <ft2build.h> #include FT_FREETYPE_H FT_Library ft; FT_Face face; if (FT_Init_FreeType(&ft)) { // 处理错误 } if (FT_New_Face(ft, "font.ttf", 0, &face)) { // 处理错误 } FT_Set_Pixel_Sizes(face, 0, 48); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); for (unsigned char c = 0; c < 128; c++) { if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { // 处理错误 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); // 将纹理渲染到屏幕上 } ``` 注意:在上面的示例中,我们只是将纹理渲染到屏幕上,实际上,您需要使用一些算法来确定每个字符的位置和大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值