检测OpenGL和GLSL的错误

为什么需要检测?

  • 编译与运行GLSL的代码与普通的代码不一样,GLSL的编译发生在C++运行时

  • GLSL的代码并没有在CPU中运行而是在GPU中运行,因此操作系统并不能捕获OpenGL运行时的错误

以上两点让调试变得很困难,我们常常很难判断着色器的运行是否失败,以及为什么失败。

如何实现呢?

  • PrintShaderLog():当GLSL代码编译失败时,显示OpenGL日志内容。

  • PrintProgramLog():当GLSL链接失败时,显示OpenGL日志内容。

  • CheckOpenGLError():检查OpenGL错误标志,即是否发生OpenGL错误。(既可以用于检查GLSL代码编译错误,又可以见检查OpenGL运行时的错误,强烈推荐使用。)

相关函数:

1.glGetShaderiv - 从着色器对象返回一个参数

规范

void glGetShaderiv(GLuint shader,GLenum pname,GLint *params);

参数
shader

指定要查询的着色器对象,直接放入需要检查的着色器即可。

pname

指定着色器对象的参数。 可接受的符号名称为

(1)GL_SHADER_TYPE:

shader_type:着色器类型 用来判断并返回着色器类型,若是顶点着色器返回GL_VERTEX_SHADER,若为片元着色器返回GL_FRAGMENT_SHADER.

(2)GL_DELETE_STATUS:

detele status:删除状态 判断着色器是否被删除,是返回GL_TRUE,否则返回GL_FALSE,

(3)GL_COMPILE_STATUS:

compile_status:编译状态 用于检测编译是否成功,成功为GL_TRUE,否则为GL_FALSE.

(4)GL_INFO_LOG_LENGTH:

information log length: log是日志的意思,所以是返回着色器的信息日志的长度 用于返回着色器的信息日志的长度,包括空终止字符(即存储信息日志所需的字符缓冲区的大小)。 如果着色器没有信息日志,则返回值0。

(5)GL_SHADER_SOURCE_LENGTH:

SHADER_SOURCE_LENGTH:着色器源码长度 返回着色器源码长度,不存在则返回0;

params

函数将返回的结果存储在输入的第三个参数中,因为这个函数得到的结果有很多种,所以要单独放在第三个参数当中,所以是void glGetShaderiv而不是GLuint。

错误
GL_INVALID_ENUM: pname不是一个可接受的值。
GL_INVALID_VALUE: shader不是OpenGL生成的值。
GL_INVALID_OPERATION: 不支持着色器编译器的情况下查询pname为GL_COMPILE_STATUS,GL_INFO_LOG_LENGTH或GL_SHADER_SOURCE_LENGTH(GL_SHADER_TYPE,GL_DELETE_STATUS不会报这个错)。
GL_INVALID_OPERATION: shader没有关联着色器对象。

2.glGetShaderInfoLog ---- 返回 shader 对象的日志信息。

原型

void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog);

参数
shader

指定需要查询日志信息的 shader 对象。

maxLength

指定储存日志信息的字符缓存大小。

length

返回 infoLog(不包括空结尾符)的字符串长度。

infoLog

指定用于返回日志信息的字符数组。

描述

glGetShaderInfoLog 返回指定 shader 对象的日志信息。一个shader 对象的日志信息会在 shader 编译是修改。返回的字符串将以空字符结尾。

glGetShaderInfoLog 返回 infoLog 的maxLength最大长度的字符的能填入的日志信息。返回的是实际的字符需要的数量,不包含空结尾符,而是指定 length 的长度。如果不需要返回字符串的长度,那么 length 传入 NULL。获取需要日志信息缓存的大小,可通过 只用 GL_INFO_LOG_LENGTH 来调用 glGetShader 。

一个 shader 对象的日志信息可能包含诊断信息,警告信息,和其他关于编译操作的信息。当一个 shader 对象被创建时,它的日志信息将会被设置为一个 0 长度的字符串。

注意

一个 shader 对象的日志信息是 OpenGL 实现的编译相关过程的主要机制。因此,日志信息对于应用程序开发者在开发过程是很有帮助的,即使编译是成功的。

错误

如果 shader 参数不是 OpenGL 生成的,那么生成 GL_INVALID_VALUE 错误。

如果 shader 参数不是一个 shader 对象,那么省 GL_INVALID_OPERATION 错误。

如果 maxLength 小于0,那么生成 GL_INVALID_VALUE 错误。

glGetError 函数 (Gl.h) - Win32 apps | Microsoft Learn

具体实现代码及测试结果:

 添加三个函数
//添加三个函数
 //捕获GLSL代码编译失败信息函数
 void PrintShaderLog(unsigned int shader)
 {
     int len = 0;
     int chWrittn = 0;
     char* log;
     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
     if (len > 0)
     {
         log = (char*)malloc(len);
         glGetShaderInfoLog(shader, len, &chWrittn, log);
         std::cout << "Shader Info Log: " << log << std::endl;
         free(log);
     }
 }
 //捕获GLSL链接失败信息函数
 void PrintProgramLog(int prog)
 {
     int len = 0;
     int chWrittn = 0;
     char* log;
     glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &len);
     if (len > 0)
     {
         log = (char*)malloc(len);
         glGetProgramInfoLog(prog, len, &chWrittn, log);
         std::cout << "Program Info Log: " << std::endl;
         free(log);
     }
 }
 //检查OpenGL错误函数
 bool CheckOpenGLError()
 {
     bool foundError = false;
     int glErr = glGetError();
     while (glErr != GL_NO_ERROR)
     {
         std::cout << "glError: " << glErr << std::endl;
         foundError = true;
         glErr = glGetError();
     }
     return foundError;
 }
 ​
修改unsigned int CreateShaderProgram()函数进行测试
 
//修改unsigned int CreateShaderProgram()函数进行测试
 unsigned int CreateShaderProgram()
 {
     const char* vShaderSource =
         "#version 430 \n"
         "void main(void) \n"
         "{gl_Position = vec4(0.0, 0.0, 0.0, 1.0);}";
     //gl_Position内置的坐标变量,前三个参数对应坐标的(x,y,z),第四个参数为矩阵变换时会用到的值,变换矩阵是4x4,所有我们     //还需要添加一位才能进行矩阵的乘法
     const char* fShaderSource =
         "#version 430 \n"
         "out vec4 color; \n"
         "void main(void) \n"
         "{ if(gl_FragCoord.x < 255) color = vec4(1.0, 0.0, 1.0); else color = vec4(0.0, 0.0, 1.0, 1.0);}";
     +//捕获编译着色器时的错误
     +int vertComplied;
     +int fragComplied;
     +int linked;
     //创建顶点着色器和片段着色器
     unsigned int vShader = glCreateShader(GL_VERTEX_SHADER);
     unsigned int fShader = glCreateShader(GL_FRAGMENT_SHADER);
     //替换vShader,fShader中的源代码,使用自定义的编码
     glShaderSource(vShader, 1, &vShaderSource, NULL);
     glShaderSource(fShader, 1, &fShaderSource, NULL);
     //对着色器对象进行编译
     glCompileShader(vShader);
     +checkOpenGLError();
     +glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertComplied);
     +if (vertComplied != 1)
     +{
     +   std::cout << "vertex compilation faild" << std::endl;
     +   printShaderLog(vShader);
     +}
     glCompileShader(fShader);
     +checkOpenGLError();
     +glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragComplied);
     +if (fragComplied != 1)
     +{
     +   std::cout << "fragment compilation faild" << std::endl;
     +   printShaderLog(fShader);
     +}
     //创建一个Program链接着色器对象,执行的就是链接有着色器对象的程序
     unsigned int vfProgram = glCreateProgram();
     //把之前编译的着色器附加到程序对象上,然后用glLinkProgram链接它们
     glAttachShader(vfProgram, vShader);
     glAttachShader(vfProgram, fShader);
     glLinkProgram(vfProgram);
     +checkOpenGLError();
     +glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked);
     +if (linked != 1)
     +{
     +   std::cout << "linking faild" << std::endl;
     +   printProgramLog(vfProgram);
     +}
     return vfProgram;
 }

完整代码:

 #include <GL/glew.h>
 #include <GLFW/glfw3.h>
 #include <iostream>
 #define numVAOs 1
 unsigned int renderingProgram;
 unsigned int vao[numVAOs];//顶点数组,必须要有
 unsigned int CreateShaderProgram();
 ​
 //捕获GLSL代码编译失败信息函数
 void PrintShaderLog(unsigned int shader)
 {
     int len = 0;
     int chWrittn = 0;
     char* log;
     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
     if (len > 0)
     {
         log = (char*)malloc(len);
         glGetShaderInfoLog(shader, len, &chWrittn, log);
         std::cout << "Shader Info Log: " << log << std::endl;
         free(log);
     }
 }
 //捕获GLSL链接失败信息函数
 void PrintProgramLog(int prog)
 {
     int len = 0;
     int chWrittn = 0;
     char* log;
     glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &len);
     if (len > 0)
     {
         log = (char*)malloc(len);
         glGetProgramInfoLog(prog, len, &chWrittn, log);
         std::cout << "Program Info Log: " << std::endl;
         free(log);
     }
 }
 //检查OpenGL错误函数
 bool CheckOpenGLError()
 {
     bool foundError = false;
     int glErr = glGetError();
     while (glErr != GL_NO_ERROR)
     {
         std::cout << "glError: " << glErr << std::endl;
         foundError = true;
         glErr = glGetError();
     }
     return foundError;
 }
 ​
 void Init(GLFWwindow* window)
 {
     renderingProgram = CreateShaderProgram();
     glGenVertexArrays(numVAOs, vao);//生成顶点数组对象名称,用numVAOs存储
     glBindVertexArray(vao[0]);//绑定vao[0]
 }
 ​
 void Display(GLFWwindow* window, double currentTime)
 {
     glUseProgram(renderingProgram);//激活程序对象
     glPointSize(430.0f);//设置点的大小
     glDrawArrays(GL_POINTS, 0, 1);//绘制方式,绘制一个点
 }
 unsigned int CreateShaderProgram()
 {
     const char* vShaderSource =
         "#version 430 \n"
         "void main(void) \n"
         "{gl_Position = vec4(0.0, 0.0, 0.0, 1.0);}";
     //gl_Position内置的坐标变量,前三个参数对应坐标的(x,y,z),第四个参数为矩阵变换时会用到的值,变换矩阵是4x4,所有我们     //还需要添加一位才能进行矩阵的乘法
     const char* fShaderSource =
         "#version 430 \n"
         "out vec4 color; \n"
         "void main(void) \n"
         "{ if(gl_FragCoord.x < 255) color = vec4(1.0, 0.0, 1.0); else color = vec4(0.0, 0.0, 1.0, 1.0);}";
     
     //捕获编译着色器时的错误
     int vertComplied;
     int fragComplied;
     int linked;
     //创建顶点着色器和片段着色器
     unsigned int vShader = glCreateShader(GL_VERTEX_SHADER);
     unsigned int fShader = glCreateShader(GL_FRAGMENT_SHADER);
     //替换vShader,fShader中的源代码,使用自定义的编码
     glShaderSource(vShader, 1, &vShaderSource, NULL);
     glShaderSource(fShader, 1, &fShaderSource, NULL);
     //对着色器对象进行编译
     glCompileShader(vShader);
     CheckOpenGLError();
     glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertComplied);
     if (vertComplied != 1)
     {
         std::cout << "vertex compilation faild" << std::endl;
         PrintShaderLog(vShader);
     }
     glCompileShader(fShader);
     CheckOpenGLError();
     glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragComplied);
     if (fragComplied != 1)
     {
         std::cout << "fragment compilation faild" << std::endl;
         PrintShaderLog(fShader);
     }
     //创建一个Program链接着色器对象,执行的就是链接有着色器对象的程序
     unsigned int vfProgram = glCreateProgram();
     //把之前编译的着色器附加到程序对象上,然后用glLinkProgram链接它们
     glAttachShader(vfProgram, vShader);
     glAttachShader(vfProgram, fShader);
     glLinkProgram(vfProgram);
     CheckOpenGLError();
     glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked);
     if (linked != 1)
     {
         std::cout << "linking faild" << std::endl;
         PrintProgramLog(vfProgram);
     }
     //返回连接好的着色器程序
     return vfProgram;
 }
 int main()
 {
     if (!glfwInit())
         exit(EXIT_FAILURE);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
     GLFWwindow* window = glfwCreateWindow(600, 600, "Hello, world", nullptr, nullptr);
     glfwMakeContextCurrent(window);
 ​
     if (glewInit() != GLEW_OK)
         exit(EXIT_FAILURE);
     glfwSwapInterval(1);
 ​
     Init(window);
 ​
     while (!glfwWindowShouldClose(window))
     {
         Display(window, glfwGetTime());
         glfwSwapBuffers(window);
         glfwPollEvents();
     }
 ​
     glfwDestroyWindow(window);
     glfwTerminate();
     exit(EXIT_SUCCESS);
 }

测试结果输出:

image-20231124145940646

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值