文本渲染 - 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);
}