OpenGL Chan视频学习-10 Dealing with Errors in OpenGL

news2025/6/3 10:27:58

bilibili视频链接:

【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p=5&vd_source=44b77bde056381262ee55e448b9b1973

函数网站:

docs.gl

说明:

1.之后就不再单独整理网站具体函数了,网站直接翻译会更直观也会有更多注意点。直接通过csdn索引查找反而会慢。

2.代码区域会单独注释功能参数返回值和相关注意事项。

3.课程学习从4-本节,如果有些函数没有注释可以看专栏里面的前面发表的文章,一般有解释。

4.如果觉得代码注释白色字体不太直观可以直接copy到相应软件看

5.有两种版本的可供查看:注释全面的和注释简洁版的,可以在索引里面找到相关代码查看。

6.希望能帮到你。

7.有错误请跟我说明一下,可能整理的时候没有检查好

一、知识点整理

1.1 检查OpenGL错误

1.1.1方法一 glGetError

1.1.1.1网址

glGetError - OpenGL 2 - docs.glhttps://docs.gl/gl2/glGetError

1.1.1.2示例
static void GLClearError()
{
    //功能:检测OpenGL错误
    //glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。
    //注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。
    while (glGetError() != GL_NO_ERROR);
}


static void GLCheckError()
{
    //功能:检测OpenGL错误,输出错误信息
    //while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << "): " << std::endl;
    }
}

为什么要在while里面调用?

在代码中,glGetError()函数用于检测OpenGL错误,它会返回最后一次发生的错误代码,并且每次调用该函数时会清除之前的错误。因此,如果只是调用一次glGetError(),可能无法捕获所有累积的错误,因为之前的错误会被清除。

通过将glGetError()放在一个while循环中,可以不断地检查和清除OpenGL错误,直到返回GL_NO_ERROR为止。这样可以确保在执行OpenGL操作之前,所有的错误都被捕获和处理,从而帮助开发者调试和确保程序的正确性。

请注意,这个循环通常会消耗CPU资源,因为它会一直检查错误直到没有错误为止,所以在实际应用中可能需要谨慎使用。

1.1.1.3运行结果

1.1.1.4解决方法

1.log

2.将十进制转换成十六进制。右键点击error

3.进入#include <GL/glew.h>搜索500

无效的枚举

1.1.1.5注意

应该在循环中调用确保得到所有错误

1.1.1.6缺点

不显示错误行,必须自行在每个函数之前调用并检测错误

1.1.2方法二

1.1.2.1代码

ASSERT宏

//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
//这个宏接受一个参数 x,它是一个条件表达式。
//if(!(x)) __debugbreak();: 这是宏的实现部分。
// 它检查参数 x 是否为 false。
// 如果是,__debugbreak(); 会被调用,这通常会导致程序在当前位置中断,进入调试器。
#define ASSERT(x) if(!(x)) __debugbreak();

函数

static void GLClearError()
{
    //功能:检测OpenGL错误
    //glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。
    //注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。
    while (glGetError() != GL_NO_ERROR);
}


static bool GLLogError()
{
    //功能:检测OpenGL错误,输出错误信息
    //while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << "): " << std::endl;
        return false;
    }
    return true;
}

int main修改

GLClearError();
//功能:绘制三角形
glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);
ASSERT(GLLogError());
1.1.2.2运行结果

1.1.3方法二改进

1.1.3.1代码

//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
//这个宏接受一个参数 x,它是一个条件表达式。
//if(!(x)) __debugbreak();: 这是宏的实现部分。
// 它检查参数 x 是否为 false。
// 如果是,__debugbreak(); 会被调用,这通常会导致程序在当前位置中断,进入调试器。
#define ASSERT(x) if(!(x)) __debugbreak();
//功能:定义了一个宏 GLCall(x),用于调试 OpenGL 应用程序时检查和处理 OpenGL 错误。
//x 是该宏接受的一个参数,通常是一个 OpenGL 函数调用。
//GLClearError();: 宏展开后首先会调用 GLClearError() 函数,清除 OpenGL 错误缓冲中的所有错误。
//x;: 接着是传入的 OpenGL 函数调用。
// 例如,如果宏被调用为 GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));,
// 那么这里将展开为实际的 OpenGL 函数调用 glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);。
//ASSERT(GLLogError(#x, __FILE__, __LINE__));: 
// #x 将 OpenGL 函数调用转换为字符串。
// __FILE__ 和 __LINE__ 是预定义的宏,分别包含当前源文件名和行号。
// GLLogError() 函数会输出任何检测到的错误信息。如果错误发生,ASSERT 宏将导致程序中断并进入调试器。


// 整体思路:
//1.错误清除:
//GLClearError() 函数通过循环调用 glGetError() 清除 OpenGL 错误缓冲中的所有错误,直到 glGetError() 返回 GL_NO_ERROR。
//2.OpenGL 函数调用:
//x; 是传入的实际 OpenGL 函数调用,比如 glDrawElements、glBindBuffer 等。
//3.错误检查和断言:
//GLLogError() 函数再次调用 glGetError() 来检查是否有新的错误发生。如果检测到错误,它会将错误代码和错误信息输出到标准输出,并返回 false。
//ASSERT(GLLogError()); 宏用于验证 GLLogError() 的返回值。如果返回值为 false(即有错误发生),程序将中断并进入调试器,这样开发者可以更容易地找到并修复问题。
#define GLCall(x) GLClearError();\
    x;\
    ASSERT(GLLogError(#x,__FILE__,__LINE__));

函数

static void GLClearError()
{
    //功能:检测OpenGL错误
    //glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。
    //注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。
    while (glGetError() != GL_NO_ERROR);
}

//参数:1.function: 发生错误的函数名
//     2.file: 发生错误的文件名
//     3.line: 发生错误的行号
//功能:输出OpenGL错误信息
static bool GLLogError(const char* function, const char* file, int line)
{
    //功能:检测OpenGL错误,输出错误信息
    //while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << "): " << function << 
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}

int main调用修改

//功能:绘制三角形
GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));
1.1.3.2运行结果

1.1.4方法三——glDebugMessageCallback
1.1.4.1网址

glDebugMessageCallback - OpenGL 4 - docs.glhttps://docs.gl/gl4/glDebugMessageCallback

二、完整代码

2.1完全注释代码

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include<iostream>
#include<fstream>
#include<string>
#include<sstream>

//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
//这个宏接受一个参数 x,它是一个条件表达式。
//if(!(x)) __debugbreak();: 这是宏的实现部分。
// 它检查参数 x 是否为 false。
// 如果是,__debugbreak(); 会被调用,这通常会导致程序在当前位置中断,进入调试器。
#define ASSERT(x) if(!(x)) __debugbreak();
//功能:定义了一个宏 GLCall(x),用于调试 OpenGL 应用程序时检查和处理 OpenGL 错误。
//x 是该宏接受的一个参数,通常是一个 OpenGL 函数调用。
//GLClearError();: 宏展开后首先会调用 GLClearError() 函数,清除 OpenGL 错误缓冲中的所有错误。
//x;: 接着是传入的 OpenGL 函数调用。
// 例如,如果宏被调用为 GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));,
// 那么这里将展开为实际的 OpenGL 函数调用 glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);。
//ASSERT(GLLogError(#x, __FILE__, __LINE__));: 
// #x 将 OpenGL 函数调用转换为字符串。
// __FILE__ 和 __LINE__ 是预定义的宏,分别包含当前源文件名和行号。
// GLLogError() 函数会输出任何检测到的错误信息。如果错误发生,ASSERT 宏将导致程序中断并进入调试器。


// 整体思路:
//1.错误清除:
//GLClearError() 函数通过循环调用 glGetError() 清除 OpenGL 错误缓冲中的所有错误,直到 glGetError() 返回 GL_NO_ERROR。
//2.OpenGL 函数调用:
//x; 是传入的实际 OpenGL 函数调用,比如 glDrawElements、glBindBuffer 等。
//3.错误检查和断言:
//GLLogError() 函数再次调用 glGetError() 来检查是否有新的错误发生。如果检测到错误,它会将错误代码和错误信息输出到标准输出,并返回 false。
//ASSERT(GLLogError()); 宏用于验证 GLLogError() 的返回值。如果返回值为 false(即有错误发生),程序将中断并进入调试器,这样开发者可以更容易地找到并修复问题。
#define GLCall(x) GLClearError();\
    x;\
    ASSERT(GLLogError(#x,__FILE__,__LINE__));

static void GLClearError()
{
    //功能:检测OpenGL错误
    //glGetError()函数返回最后一次发生的错误代码,如果没有错误发生,则返回GL_NO_ERROR。
    //注意:在调用glGetError()函数之前,必须先调用glClearError()函数,否则会导致错误计数器不清零。
    while (glGetError() != GL_NO_ERROR);
}

//参数:1.function: 发生错误的函数名
//     2.file: 发生错误的文件名
//     3.line: 发生错误的行号
//功能:输出OpenGL错误信息
static bool GLLogError(const char* function, const char* file, int line)
{
    //功能:检测OpenGL错误,输出错误信息
    //while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << "): " << function << 
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//功能:定义ShaderProgramSource结构体,用于存储着色器代码
struct ShaderProgramSource
{
    std::string VertexSource;
    std::string FragmentSource;
};


//功能:解析着色器代码文件。
static ShaderProgramSource ParseShader(const std::string& filepath)
{
    //功能:打开文件流
    std::ifstream stream(filepath);

    //定义着色器类型
    enum  class ShaderType
    {
        NONE=-1,VERTEX=0,FRAGMENT=1
    };

    //该变量用于存储着色器代码
    std::string line;
    //该变量用于存储着色器类型
    std::stringstream ss[2];
    //该变量是当前着色器类型
    ShaderType type = ShaderType::NONE;
    //功能:读取文件中的每一行内容,直到文件结束
    while (getline(stream, line))
    {
        //如果当前行包含#shader,则说明接下来是着色器代码
        if (line.find("#shader") != std::string::npos)
        {
            //如果当前行包含vertex,则说明接下来是顶点着色器代码
            if (line.find("vertex") != std::string::npos)
            {
                type = ShaderType::VERTEX;
            }
            else if (line.find("fragment") != std::string::npos)
            {
                type = ShaderType::FRAGMENT;
            }
        }
        else
        {
            //否则,将当前行添加到对应着色器代码的stringstream中
            ss[(int)type] << line << '\n';
        }
    }
    //返回ShaderProgramSource结构体
    return { ss[0].str(), ss[1].str() };
}


//功能:编译着色器代码
static unsigned int CompilesShader(unsigned int type, const std::string& source)
{
    //功能:创建着色器对象
    unsigned int id = glCreateShader(type);
    //功能:设置着色器源代码.
    const char* src = source.c_str();
    //功能:替换着色器对象中的源代码。将该id的指定着色器的源代码设置为src指针指向的字符串
    glShaderSource(id, 1, &src, nullptr);
    //功能:编译着色器对象的源代码
    glCompileShader(id);

    //设置返回着色器的对象ID
    int result;
    //功能:从着色器对象返回一个参数,表示编译是否成功。
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);

    //如果编译失败,则输出错误信息
    if (result == GL_FALSE)
    {
        int length;
        //功能:获取编译错误信息的长度
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        //分配内存,用于存储编译错误信息
        char* message = (char*)alloca(length*sizeof(char));
        //功能:获取编译错误信息
        glGetShaderInfoLog(id, length, &length, message);
        std::cout << "Failed to compile shader!" << (type == GL_VERTEX_SHADER? "Vertex" : "Fragment") << "shader!" << std::endl;
        std::cout << message << std::endl;
        //删除着色器对象
        glDeleteShader(id);
        return 0;
    }

    //TODO:错误处理ing
    return id;
}


//功能:创建着色器程序
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
    //创建程序对象
    unsigned int program = glCreateProgram();
    //编译顶点着色器对象
    unsigned int vs = CompilesShader(GL_VERTEX_SHADER, vertexShader);
    //编译片段着色器对象
    unsigned int fs = CompilesShader(GL_FRAGMENT_SHADER, fragmentShader);

    //功能:将编译好的着色器对象附加到程序对象中
    glAttachShader(program, vs);
    glAttachShader(program, fs);

    //功能:链接程序对象
    glLinkProgram(program);

    //功能:验证着色器程序对象是否可以在当前OpenGL状态中执行。检查着色器程序的完整性和可执行性。
    glValidateProgram(program);

    //删除着色器对象,因为它们已经被链接到程序对象中
    glDeleteShader(vs);
    glDeleteShader(fs);

    //返回着色器程序
    return program;
}


int main(void)
{
    GLFWwindow* window;

    //初始化glfw
    if (!glfwInit())
        return -1;

    //创建一个窗口模式的窗口并设置OpenGL上下文
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)//如果窗口创建失败,则终止程序
    {
        glfwTerminate();//释放glfw资源
        return -1;
    }

    //设置当前窗口的上下文,之后所有的OpenGL调用都会在这个上下文中进行
    glfwMakeContextCurrent(window);

    //初始化GLEW
    if (glewInit() != GLEW_OK)
        std::cout << "Error!" << std::endl;

    //打印OpenGL版本信息
    std::cout << glGetString(GL_VERSION) << std::endl;

    //准备数据
    float position[] = {
        -0.5f, -0.5f,
        0.5f, -0.5f,
        0.5f,0.5f,
        -0.5f,0.5f,
    };

    //定义顶点索引缓存,用于标定顶点顺序
    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };

    
    //定义缓冲区对象
    unsigned int buffer;
    //功能:生成缓冲区对象,并将数据写入缓冲区
    glGenBuffers(1, &buffer);
    //功能:将缓冲区对象绑定到目标
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    //功能:将数据写入缓冲区
    glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);

    //功能:配置顶点属性指针
    glEnableVertexAttribArray(0);
    //功能:指定顶点属性数组的索引、大小、数据类型、是否归一化、偏移量、数据指针
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);
    

    //索引缓冲区对象
    unsigned int ibo;
    //功能:生成缓冲区对象,并将数据写入缓冲区
    glGenBuffers(1, &ibo);
    //功能:将缓冲区对象绑定到目标.没有绑定为数组缓冲区
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    //功能:将数据写入缓冲区
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);


    //解析着色器代码文件
    ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");
    std::string vertexShader = source.VertexSource;
    std::string fragmentShader = source.FragmentSource;
    //创建着色器程序
    unsigned int shader = CreateShader(vertexShader, fragmentShader);
    //使用着色器程序
    glUseProgram(shader);

    //渲染循环,直到窗口被关闭
    while (!glfwWindowShouldClose(window))
    {
        //清除颜色缓冲区
        glClear(GL_COLOR_BUFFER_BIT);
        
        //功能:绘制三角形
        GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));

        //刷新缓冲区并交换窗口
        glfwSwapBuffers(window);

        //处理窗口事件,如键盘输入、鼠标移动等
        glfwPollEvents();
    }

    //删除着色器程序
    //glDeleteProgram(shader);

    //释放 GLFW 库占用的所有资源。
    glfwTerminate();
    return 0;
}

2.2简洁注释代码

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include<iostream>
#include<fstream>
#include<string>
#include<sstream>

//功能:定义宏,用于在调试过程中进行条件断言,检测OpenGL错误
#define ASSERT(x) if(!(x)) __debugbreak();
//功能:定义了一个宏 GLCall(x),用于调试 OpenGL 应用程序时检查和处理 OpenGL 错误。
#define GLCall(x) GLClearError();\
    x;\
    ASSERT(GLLogError(#x,__FILE__,__LINE__));

static void GLClearError()
{
    //功能:检测OpenGL错误
    while (glGetError() != GL_NO_ERROR);
}

//参数:1.function: 发生错误的函数名
static bool GLLogError(const char* function, const char* file, int line)
{
    //功能:检测OpenGL错误,输出错误信息
    //while循环,直到glGetError()返回GL_NO_ERROR,表示没有错误发生。
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << "): " << function << 
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//功能:定义ShaderProgramSource结构体,用于存储着色器代码
struct ShaderProgramSource
{
    std::string VertexSource;
    std::string FragmentSource;
};


//功能:解析着色器代码文件。
static ShaderProgramSource ParseShader(const std::string& filepath)
{
    //功能:打开文件流
    std::ifstream stream(filepath);

    //定义着色器类型
    enum  class ShaderType
    {
        NONE=-1,VERTEX=0,FRAGMENT=1
    };

    //该变量用于存储着色器代码
    std::string line;
    //该变量用于存储着色器类型
    std::stringstream ss[2];
    //该变量是当前着色器类型
    ShaderType type = ShaderType::NONE;
    //功能:读取文件中的每一行内容,直到文件结束
    while (getline(stream, line))
    {
        //如果当前行包含#shader,则说明接下来是着色器代码
        if (line.find("#shader") != std::string::npos)
        {
            //如果当前行包含vertex,则说明接下来是顶点着色器代码
            if (line.find("vertex") != std::string::npos)
            {
                type = ShaderType::VERTEX;
            }
            else if (line.find("fragment") != std::string::npos)
            {
                type = ShaderType::FRAGMENT;
            }
        }
        else
        {
            //否则,将当前行添加到对应着色器代码的stringstream中
            ss[(int)type] << line << '\n';
        }
    }
    //返回ShaderProgramSource结构体
    return { ss[0].str(), ss[1].str() };
}


//功能:编译着色器代码
static unsigned int CompilesShader(unsigned int type, const std::string& source)
{
    //功能:创建着色器对象
    unsigned int id = glCreateShader(type);
    //功能:设置着色器源代码.
    const char* src = source.c_str();
    //功能:替换着色器对象中的源代码。将该id的指定着色器的源代码设置为src指针指向的字符串
    glShaderSource(id, 1, &src, nullptr);
    //功能:编译着色器对象的源代码
    glCompileShader(id);

    //设置返回着色器的对象ID
    int result;
    //功能:从着色器对象返回一个参数,表示编译是否成功。
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);

    //如果编译失败,则输出错误信息
    if (result == GL_FALSE)
    {
        int length;
        //功能:获取编译错误信息的长度
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        //分配内存,用于存储编译错误信息
        char* message = (char*)alloca(length*sizeof(char));
        //功能:获取编译错误信息
        glGetShaderInfoLog(id, length, &length, message);
        std::cout << "Failed to compile shader!" << (type == GL_VERTEX_SHADER? "Vertex" : "Fragment") << "shader!" << std::endl;
        std::cout << message << std::endl;
        //删除着色器对象
        glDeleteShader(id);
        return 0;
    }

    //TODO:错误处理ing
    return id;
}


//功能:创建着色器程序
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
    //创建程序对象
    unsigned int program = glCreateProgram();
    //编译顶点着色器对象
    unsigned int vs = CompilesShader(GL_VERTEX_SHADER, vertexShader);
    //编译片段着色器对象
    unsigned int fs = CompilesShader(GL_FRAGMENT_SHADER, fragmentShader);

    //功能:将编译好的着色器对象附加到程序对象中
    glAttachShader(program, vs);
    glAttachShader(program, fs);

    //功能:链接程序对象
    glLinkProgram(program);

    //功能:验证着色器程序对象是否可以在当前OpenGL状态中执行。检查着色器程序的完整性和可执行性。
    glValidateProgram(program);

    //删除着色器对象,因为它们已经被链接到程序对象中
    glDeleteShader(vs);
    glDeleteShader(fs);

    //返回着色器程序
    return program;
}


int main(void)
{
    GLFWwindow* window;

    //初始化glfw
    if (!glfwInit())
        return -1;

    //创建一个窗口模式的窗口并设置OpenGL上下文
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)//如果窗口创建失败,则终止程序
    {
        glfwTerminate();//释放glfw资源
        return -1;
    }

    //设置当前窗口的上下文,之后所有的OpenGL调用都会在这个上下文中进行
    glfwMakeContextCurrent(window);

    //初始化GLEW
    if (glewInit() != GLEW_OK)
        std::cout << "Error!" << std::endl;

    //打印OpenGL版本信息
    std::cout << glGetString(GL_VERSION) << std::endl;

    //准备数据
    float position[] = {
        -0.5f, -0.5f,
        0.5f, -0.5f,
        0.5f,0.5f,
        -0.5f,0.5f,
    };

    //定义顶点索引缓存,用于标定顶点顺序
    unsigned int indices[] = {
        0,1,2,
        2,3,0
    };

    
    //定义缓冲区对象
    unsigned int buffer;
    //功能:生成缓冲区对象,并将数据写入缓冲区
    glGenBuffers(1, &buffer);
    //功能:将缓冲区对象绑定到目标
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    //功能:将数据写入缓冲区
    glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);

    //功能:配置顶点属性指针
    glEnableVertexAttribArray(0);
    //功能:指定顶点属性数组的索引、大小、数据类型、是否归一化、偏移量、数据指针
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);
    

    //索引缓冲区对象
    unsigned int ibo;
    //功能:生成缓冲区对象,并将数据写入缓冲区
    glGenBuffers(1, &ibo);
    //功能:将缓冲区对象绑定到目标.没有绑定为数组缓冲区
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    //功能:将数据写入缓冲区
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);


    //解析着色器代码文件
    ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");
    std::string vertexShader = source.VertexSource;
    std::string fragmentShader = source.FragmentSource;
    //创建着色器程序
    unsigned int shader = CreateShader(vertexShader, fragmentShader);
    //使用着色器程序
    glUseProgram(shader);

    //渲染循环,直到窗口被关闭
    while (!glfwWindowShouldClose(window))
    {
        //清除颜色缓冲区
        glClear(GL_COLOR_BUFFER_BIT);
        
        //功能:绘制三角形
        GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));

        //刷新缓冲区并交换窗口
        glfwSwapBuffers(window);

        //处理窗口事件,如键盘输入、鼠标移动等
        glfwPollEvents();
    }

    //删除着色器程序
    //glDeleteProgram(shader);

    //释放 GLFW 库占用的所有资源。
    glfwTerminate();
    return 0;
}

2.3运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2392914.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美团启动618大促,线上消费节被即时零售传导到线下了?

首先&#xff0c;从市场推广与消费者吸引的角度来看&#xff0c;美团通过联合众多品牌开展大规模促销活动&#xff0c;并发放高额优惠券包&#xff0c;旨在吸引更多消费者参与购物。这种策略有助于提高平台的活跃度和交易量&#xff0c;同时也能够增强用户粘性。对于消费者而言…

搭建 Select 三级联动架构-东方仙盟插件开发 JavaScript ——仙盟创梦IDE

三级级联开卡必要性 在 “东方仙盟” 相关插件开发中&#xff0c;使用原生 HTML 和 JavaScript 实现三级联动选择&#xff08;如村庄 - 建筑 - 单元的选择&#xff09;有以下好处和意义&#xff0c;学校管理&#xff1a; 对游戏体验的提升 增强交互性&#xff1a;玩家能够通…

服务器如何配置防火墙管理端口访问?

配置服务器防火墙来管理端口访问&#xff0c;是保障云服务器安全的核心步骤。下面我将根据你使用的不同操作系统&#xff08;Linux: Ubuntu/Debian/CentOS&#xff1b;Windows Server&#xff09;介绍常用防火墙配置方法。 ✅ 一、Linux 防火墙配置&#xff08;UFW / firewalld…

Spring Boot+Activiti7入坑指南初阶版

介绍  Activiti 是一个轻量级工作流程和业务流程管理 (BPM) 平台,面向业务人员、开发人员和系统管理员。其核心是一个超快且坚如磐石的 Java BPMN 2 流程引擎。它是开源的,并根据 Apache 许可证分发。Activiti 可以在任何 Java 应用程序、服务器、集群或云中运行。它与 Spri…

如何在 Odoo 18 中创建 PDF 报告

如何在 Odoo 18 中创建 PDF 报告 Qweb 是 Odoo 强大的模板引擎&#xff0c;旨在轻松将 XML 数据转换为 HTML 文档。其功能特性包括基于属性的自定义、条件逻辑、动态内容插入及多样化的报告模板选项。这种多功能性使 Qweb 成为制作个性化、视觉吸引力强的报告、电子邮件和文档…

【ROS2实体机械臂驱动】rokae xCoreSDK Python测试使用

【ROS2实体机械臂驱动】rokae xCoreSDK Python测试使用 文章目录 前言正文配置环境下载源码配置环境变量测试运行修改点说明实际运行情况 参考 前言 本文用来记录 xCoreSDK-Python的调用使用1。 正文 配置环境 配置开发环境&#xff0c;这里使用conda做python环境管理&…

VLC-QT 网页播放RTSP

先看效果图,代码在文章末尾,包含源码,vlc-qt完整的库 环境说明:VS 2017 QTQt5.13.0 MSVC2017 32位 将vlc_install 目录下的bin,include,lib里所有的东西分别放在qt目录下 bin -> C:\Qt\Qt5.13.0\5.13.0\msvc2017\bin include->C:\Qt\Qt5.13.0\5.13.0\msvc201…

【航天远景 MapMatrix 精品教程】08 Pix4d空三成果导入MapMatrix

【航天远景 MapMatrix 精品教程】08 Pix4d空三成果导入MapMatrix 文章目录 【航天远景 MapMatrix 精品教程】08 Pix4d空三成果导入MapMatrix一、资料准备1.去畸变影像2.相机文件3.外方位元素二、创建工程1.新建工程2.导入照片3.编辑相机文件4.编辑外方位元素文件,导入外方位元…

创建型设计模式之Prototype(原型)

创建型设计模式之Prototype&#xff08;原型&#xff09; 摘要&#xff1a; Prototype&#xff08;原型&#xff09;设计模式通过复制现有对象来创建新对象&#xff0c;避免重复初始化操作。该模式包含Prototype接口声明克隆方法、ConcretePrototype实现具体克隆逻辑&#xff…

JNI开发流程

一. 引言 最近在做一个自己的项目&#xff0c;就是基于FastDDS封装一套JAVA库&#xff0c;让android和java应用可以使用dds的功能。 由于FastDDS是使用C编写的开源库&#xff0c;因此java的类库想要调用FastDDS的接口&#xff0c;需要额外编写一个JNI层的动态库对FastDDS的接口…

STM32G4 电机外设篇(二) VOFA + ADC + OPAMP

目录 一、STM32G4 电机外设篇&#xff08;二&#xff09; VOFA ADC OPAMP1 VOFA1.1 VOFA上位机显示波形 2 ADC2.1 用ADC规则组对板载电压和电位器进行采样 3 OPAMP&#xff08;运放&#xff09;3.1 结合STM32内部运放和ADC来完成对三相电流的采样3.2 运放电路分析 附学习参考…

微服务难题?Nacos服务发现来救场

文章目录 前言1.什么是服务发现2.Nacos 闪亮登场2.1 服务注册2.2 服务发现 3.Nacos 的优势3.1 简单易用3.2 高可用3.3 动态配置 4.实战演练4.1安装 Nacos4.2 服务注册与发现示例代码&#xff08;以 Spring Boot 为例&#xff09; 总结 前言 大家好&#xff0c;我是沛哥儿。今天…

C# 结合PaddleOCRSharp搭建Http网络服务

Windows打开端口&#xff1a; 控制面板 > 系统和安全 > 防火墙> 高级设置 → 入站规则 → 右侧选择 → 新建规则 → 端口 → 协议类型 TCP→ 端口 using System; using System.Drawing; using System.IO; using System.Net; using System.Text; using System.Threadi…

【连接器专题】SD卡座规格书审查需要审哪些方面?

在审查SD卡座规格书时,我们需要考虑哪些方面? 首先在拿到一份SD卡座的详细规格书时,一般供应商给到的规格书中包括了一些基础信息、产品图纸信息、技术参数信息,同时有些供应商会给出产品可靠性测试报告。因此我们会从这几个要素去看规格书。 基础信息 基础信息一般会给变更…

eBest智能价格引擎系统 助力屈臣氏饮料落地「价格大脑」+「智慧通路」数字基建​

从价格策略到终端执行&#xff0c;数字化正在重构饮料行业竞争壁垒&#xff01; 近日&#xff0c;eBest为屈臣氏饮料提供的智能价格引擎系统已正式上线并投入运营。同时&#xff0c;基于eBest SFA方案且与屈臣氏饮料业务场景深度耦合的Smart Field Operation智慧通路项目正式启…

Kettle 远程mysql 表导入到 hadoop hive

kettle 远程mysql 表导入到 hadoop hive &#xff08;教学用 &#xff09; 文章目录 kettle 远程mysql 表导入到 hadoop hive创建 对象 执行 SQL 语句 -mysql 导出 CSV格式CSV 文件远程上传到 HDFS运行 SSH 命令远程登录 run SSH 并执行 hadoop fs -put 建表和加载数据总结 创…

完整解析 Linux Kdump Crash Kernel 工作原理和实操步骤

完整解析 Linux Kdump Crash Kernel 工作原理和实操步骤 一、前言 在使用 Linux 操作系统进行内核开发或者系统维护时&#xff0c;内核 panic 是最常见的系统崩溃环节。如果想要在内核崩溃后立即分析环境和输出内核内存 dump&#xff0c;Kdump crashkernel 是最接近完美的解…

菜鸟之路Day36一一Web开发综合案例(部门管理)

菜鸟之路Day36一一Web开发综合案例&#xff08;部门管理&#xff09; 作者&#xff1a;blue 时间&#xff1a;2025.5.28 文章目录 菜鸟之路Day36一一Web开发综合案例&#xff08;部门管理&#xff09;一.环境搭建二.开发规范三.部门管理3.1查询3.2删除3.3新增3.3修改根据id来…

第 1 章:学习起步

1. React Native前置知识要求 在开始学习React Native之前&#xff0c;有一些前置知识你需要了解。不过别担心&#xff0c;我会带你逐步掌握这些内容&#xff0c;让你顺利入门。 1.1. JavaScript是必须掌握的 学习React Native&#xff0c;JavaScript是基础。你需要了解Java…

SQL查询——大厂面试真题

前言 本文总结了SQLite数据库的核心操作要点&#xff1a;1. 基础语法&#xff1a;SQL语句不区分大小写&#xff0c;多语句需用分号分隔&#xff0c;支持多种注释方式2. 表操作&#xff1a;包括创建表&#xff08;定义主键、非空约束等&#xff09;、插入/更新/删除数据、添加/…