为什么需要检测?
-
编译与运行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);
}