You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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,核心问题有三个:

  1. VAO和VBO未初始化:全局变量VAO、VBO仅声明未创建,也未配置顶点属性指针。OpenGL Core Profile下必须绑定VAO才能执行绘制操作。
  2. 字符纹理未加载:仅加载了'X'字符,未生成"Hello World!"所需的全部字符,Characters映射为空,访问Characters[*c]会创建无效的Character对象,导致纹理ID非法。
  3. 投影矩阵未设置:顶点着色器中的projection uniform未赋值,导致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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:57:31