使用GLFW3与FreeType绘制文本时出现0x502(1282)错误
OpenGL FreeType文本渲染0x502错误修复
我之前用C#/Unity开发游戏,现在转用C++结合OpenGL开发自制游戏,尝试实现屏幕文本渲染功能。采用FreeType方案时,在RenderText函数中的glBufferSubData和glDrawArrays操作后出现0x502错误(GL_INVALID_OPERATION),调整变量、库版本及Shader代码后仍未解决,所有文件路径均正确,相关代码如下:
问题代码
main.cpp
#include <iostream> #include <ft2build.h> #include FT_FREETYPE_H #include <GL/glew.h> #include <GLFW/glfw3.h> #include <glm/glm.hpp> #include <vector> #include <fstream> #include <sstream> #include <map> void checkGlError(const char* op) { for (GLint error = glGetError(); error; error = glGetError()) { std::cout << "after " << op << " glError (0x" << std::hex << error << ")" << std::endl; } } struct Character { GLuint TextureID; // ID handle of the glyph texture glm::ivec2 Size; // Size of glyph glm::ivec2 Bearing; // Offset from baseline to left/top of glyph GLuint Advance; // Offset to advance to next glyph }; std::map<GLchar, Character> Characters; GLuint VAO, VBO; GLuint compileShader(const GLchar *shaderCode, GLenum shaderType) { GLuint shader = glCreateShader(shaderType); glShaderSource(shader, 1, &shaderCode, NULL); glCompileShader(shader); GLint success; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { GLchar infoLog[512]; glGetShaderInfoLog(shader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << std::endl; } if(success) { std::cout << "Shader compiled successfully" << std::endl; } return shader; } GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader) { GLuint program = glCreateProgram(); glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glLinkProgram(program); GLint success; glGetProgramiv(program, GL_LINK_STATUS, &success); if (!success) { GLchar infoLog[512]; glGetProgramInfoLog(program, 512, NULL, infoLog); std::cout << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } if (success) { std::cout << "Program linked successfully" << std::endl; } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return program; } void RenderText(GLuint shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) { // Activate corresponding render state glUseProgram(shader); checkGlError("glUseProgram"); glUniform3f(glGetUniformLocation(shader, "textColor"), color.x, color.y, color.z); checkGlError("glUniform3f"); glActiveTexture(GL_TEXTURE0); checkGlError("glActiveTexture"); glBindVertexArray(VAO); checkGlError("glBindVertexArray"); // Iterate through all characters std::string::const_iterator c; for (c = text.begin(); c != text.end(); c++) { Character ch = Characters[*c]; GLfloat xpos = x + ch.Bearing.x * scale; GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale; GLfloat w = ch.Size.x * scale; GLfloat h = ch.Size.y * scale; // Update VBO for each character 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 } }; // Render glyph texture over quad glBindTexture(GL_TEXTURE_2D, ch.TextureID); checkGlError("glBindTexture"); // Update content of VBO memory glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); checkGlError("glBufferSubData"); glBindBuffer(GL_ARRAY_BUFFER, 0); // Render quad glDrawArrays(GL_TRIANGLES, 0, 6); checkGlError("glDrawArrays"); x += (ch.Advance >> 6) * scale; } glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } std::string readShaderFromFile(const std::string& filepath) { std::ifstream shaderFile(filepath); std::stringstream shaderData; shaderData << shaderFile.rdbuf(); shaderFile.close(); return shaderData.str(); } int main() { if (!glfwInit()) { // Initialization failed return -1; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X #endif GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); if (!window) { // Window or OpenGL context creation failed glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // GLEW initialization glewExperimental = GL_TRUE; // Needed in core profile if (glewInit() != GLEW_OK) { std::cout << "Failed to initialize GLEW" << std::endl; return -1; } FT_Library ft; if (FT_Init_FreeType(&ft)) { std::cout << "Could not init FreeType Library" << std::endl; return -1; } FT_Face face; if (FT_New_Face(ft, "../Arial.ttf", 0, &face)) { std::cout << "Failed to load font" << std::endl; return -1; } FT_Set_Pixel_Sizes(face, 0, 48); if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) { std::cout << "Failed to load Glyph" << std::endl; return -1; } // Compile shaders and link them into a program std::string vertexShaderSrc = readShaderFromFile("../shader.vs"); std::string fragmentShaderSrc = readShaderFromFile("../shader.fs"); GLuint vertexShader = compileShader(vertexShaderSrc.c_str(), GL_VERTEX_SHADER); GLuint fragmentShader = compileShader(fragmentShaderSrc.c_str(), GL_FRAGMENT_SHADER); GLuint shaderProgram = linkProgram(vertexShader, fragmentShader); while (!glfwWindowShouldClose(window)) { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); std::cout << glGetError() << std::endl; RenderText(shaderProgram, "Hello World!", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f)); glfwPollEvents(); glfwSwapBuffers(window); } return 0; }
片段着色器(shader.fs)
#version 330 core in vec2 TexCoords; out vec4 color; uniform sampler2D text; uniform vec3 textColor; void main() { vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r); color = vec4(textColor, 1.0) * sampled; }
顶点着色器(shader.vs)
#version 330 core layout (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; }
错误原因
0x502对应GL_INVALID_OPERATION,核心问题有三个:
- VAO和VBO未初始化:全局变量
VAO、VBO仅声明未创建,也未配置顶点属性指针。OpenGL Core Profile下必须绑定VAO才能执行绘制操作。 - 字符纹理未加载:仅加载了'X'字符,未生成"Hello World!"所需的全部字符,
Characters映射为空,访问Characters[*c]会创建无效的Character对象,导致纹理ID非法。 - 投影矩阵未设置:顶点着色器中的
projectionuniform未赋值,导致gl_Position计算错误,触发渲染操作无效。
修复步骤
1. 初始化VAO和VBO
在main函数编译完Shader程序后,添加VAO/VBO初始化代码:
// 初始化VAO和VBO glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); // 分配动态缓冲区空间(6个顶点×4个float) glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW); // 配置顶点属性:location 0为vec4(2个位置分量+2个纹理坐标分量) glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0);
2. 加载完整ASCII字符集
在FT_Set_Pixel_Sizes之后,添加字符纹理加载代码:
// 禁用字节对齐限制,适配FreeType的单通道纹理 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 加载ASCII可打印字符 for (unsigned char c = 0; c < 128; c++) { if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { std::cout << "Failed to load Glyph: " << c << std::endl; 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); // 存储字符信息到映射表 Character character = { texture, glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), static_cast<GLuint>(face->glyph->advance.x) }; Characters.insert(std::pair<GLchar, Character>(c, character)); } // 恢复默认字节对齐 glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 释放FreeType资源 FT_Done_Face(face); FT_Done_FreeType(ft);
3. 设置正交投影矩阵
在main函数初始化窗口后,创建并传递正交投影矩阵到Shader:
// 创建适配窗口的正交投影矩阵 glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(800), 0.0f, static_cast<GLfloat>(600)); // 传递投影矩阵到Shader glUseProgram(shaderProgram); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
4. 启用混合(可选,优化文本显示)
为了正确显示文本的透明部分,在main函数初始化时添加混合启用代码:
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
内容的提问来源于stack exchange,提问作者Fab boy
相关产品推荐
相关产品推荐

