检测OpenGL和GLSL错误
1 #include <GL/glew.h> 2 #include <GLFW/glfw3.h> 3 #include <iostream> 4 5 using namespace std; 6 7 const int numVAOs = 1; 8 GLuint renderingProgram; 9 GLuint vao[numVAOs]; 10 11 // 当 GLSL 代码编译失败时,显示 OpenGL 日志内容。 12 void printShaderLog(GLuint shader) 13 { 14 int len = 0; 15 int chWrittn = 0; 16 char* log; 17 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); 18 if (len > 0) 19 { 20 log = (char*)malloc(len); 21 glGetShaderInfoLog(shader, len, &chWrittn, log); 22 cout << "Shader Info Log: " << log << endl; 23 free(log); 24 } 25 } 26 27 // 当 GLSL 链接失败时,显示 OpenGL 日志内容 28 void printProgramLog(int prog) { 29 int len = 0; 30 int chWrittn = 0; 31 char* log; 32 glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); 33 if (len > 0) { 34 log = (char*)malloc(len); 35 glGetProgramInfoLog(prog, len, &chWrittn, log); 36 cout << "Program Info Log: " << log << endl; 37 free(log); 38 } 39 } 40 // 检查 OpenGL 错误标志,即是否发生 OpenGL 错误 41 bool checkOpenGLError() { 42 bool foundError = false; 43 int glErr = glGetError(); 44 while (glErr != GL_NO_ERROR) { 45 cout << "glError: " << glErr << endl; 46 foundError = true; 47 glErr = glGetError(); 48 } 49 return foundError; 50 } 51 52 53 /** 54 * @brief 创建着色器,返回一个整数ID作为索引号,便于后续引用 55 * OpenGL中要使用一个shader需要如下几步: 56 * 1.使用glCreateShader()方法创建一个空的着色器对象,用来接收源代码并进行编译 57 * 2.使用glShaderSource()方法将着色器源码传递给着色器对象,以便保留该源代码的副本 58 * 3.使用glCompileShader()方法对着色器对象中包含的任何源代码进行编译 59 * 4.使用glCreateProgram()方法创建一个着色器程序,我们可以将着色器对象附加到该对象 60 * 5.使用glAttachShader()方法将着色器附加到着色器程序 61 * 6.使用glLinkProgram()方法将所有附加到程序对象的着色器对象链接在一起 62 * 7.使用glDeleteShader()方法删除着色器对象。一旦着色器链接到一个程序对象,程序将包含二进制代码,不再需要着色器了 63 **/ 64 GLuint createShaderProgram() 65 { 66 GLint vertCompiled; 67 GLint fragCompiled; 68 GLint linked; 69 70 // 定义定点着色器源码 71 const char* vshaderSource = 72 // 声明着色器版本,与OpenGL版本同步 73 "#version 430 \n" 74 // 在顶点着色器,给 gl_Position指定out标签不是必需的,因为 gl_Position 是预定义的输出变量 75 //"out vec4 gl_Position; \n" 76 // main函数 77 "void main(void) \n" 78 // 用内置变量gl_Position在源码中硬编码一个顶点位置 79 "{ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);}"; 80 81 // 定义片段着色器源码 82 const char* fshaderSource = 83 // 声明着色器版本,与OpenGL版本同步 84 "#version 430 \n" 85 // out 标签表明color变量是输出变量 86 "out vec4 color; \n" 87 // main函数 88 "void main(void) \n" 89 // 设置颜色分量(rgba) 90 "{ if (gl_FragCoord.x < 295) color = vec4(0.0, 1.0, 0.0, 1.0); else color = vec4(0.0, 0.0, 1.0, 1.0);}"; 91 92 // 创建顶点着色器 93 GLuint vShader = glCreateShader(GL_VERTEX_SHADER); 94 // 创建片段着色器 95 GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER); 96 // 载入顶点着色器源码 97 glShaderSource(vShader, 1, &vshaderSource, nullptr); 98 // 载入片段着色器源码 99 glShaderSource(fShader, 1, &fshaderSource, nullptr); 100 // 编译顶点着色器 101 glCompileShader(vShader); 102 checkOpenGLError(); 103 glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertCompiled); 104 if (vertCompiled != 1) 105 { 106 cout << "vertex compilation failed" << endl; 107 printShaderLog(vShader); 108 } 109 // 编译片段着色器 110 glCompileShader(fShader); 111 checkOpenGLError(); 112 glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragCompiled); 113 if (fragCompiled != 1) 114 { 115 cout << "fragment compilation failed" << endl; 116 printShaderLog(fShader); 117 } 118 // 创建程序对象,OpenGL程序对象包含一系列编译过的着色器 119 GLuint vfProgram = glCreateProgram(); 120 // 加入顶点着色器 121 glAttachShader(vfProgram, vShader); 122 // 加入片段着色器 123 glAttachShader(vfProgram, fShader); 124 // 链接程序 125 glLinkProgram(vfProgram); 126 checkOpenGLError(); 127 glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked); 128 if (linked != 1) 129 { 130 cout << "linked failed" << endl; 131 printProgramLog(vfProgram); 132 } 133 // 删除着色器 134 glDeleteShader(vShader); 135 glDeleteShader(fShader); 136 return vfProgram; 137 } 138 139 void init(GLFWwindow* window) { 140 renderingProgram = createShaderProgram(); 141 // 当准备将数据集发送给管线时,数据集是以缓冲区形式发送的。这些缓冲区最后都会被存入顶点数组对象 142 // 此案例我们在顶点着色器中硬编码一个点,因此不需要缓存区 143 // 即使如此,OpenGL依然需要创建一个VAO 144 glGenVertexArrays(numVAOs, vao); 145 glBindVertexArray(vao[0]); 146 } 147 148 void display(GLFWwindow* window, double currentTime) 149 { 150 // 指定颜色缓冲区清除后填充的值 151 glClearColor(1.0, 0.0, 0.0, 1.0); 152 // 用指定颜色清除(填充)颜色缓存区 153 glClear(GL_COLOR_BUFFER_BIT); 154 // 将含有两个已编译着色器的程序载入OpenGL管线阶段,并没有运行着色器 155 glUseProgram(renderingProgram); 156 // 设置绘制点的大小,便于观察 157 glPointSize(50); 158 // 启动管线处理过程,开始绘制 159 glDrawArrays(GL_POINTS, 0, 1); 160 } 161 162 int main(void) 163 { 164 // 如果glfw初始化失败则返回 165 if (!glfwInit()) { exit(EXIT_FAILURE); } 166 // 设置OpenGL程序的主版本号 167 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 168 // 设置OpenGL程序的副版本号 169 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 170 // 借助GLFW创建一个窗口 171 GLFWwindow* window = glfwCreateWindow(600, 600, "Graphics Program With OpenGL", nullptr, nullptr); 172 // 将GLFW创建的窗口与当前OpenGL的上下文关联起来 173 glfwMakeContextCurrent(window); 174 // 如果glew初始化失败则返回,glew负责调用OpenGL相关的函数 175 if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); } 176 // 交换间隔指示了直到交换缓冲区前需要等待多少帧,通常被理解为垂直同步。 177 // 默认情况下,交换间隔为0,意味着缓冲区交换会立即发生。 178 // 在一些快速的机器上,因为屏幕保持以典型的60 - 75次每秒的速度更新,许多帧会永远看不到,所以这会浪费许多CPU和GPU周期。 179 // 而且,因为缓冲区可能会在屏幕更新的中途被交换,导致画面撕裂。 180 // 因此,应用需要代表性地设置交换间隔为1。 181 glfwSwapInterval(1); 182 183 init(window); 184 185 // 当用户点击关闭窗口按钮时会返回true 186 while (!glfwWindowShouldClose(window)) 187 { 188 display(window, glfwGetTime()); 189 // GLFW默认使用了双缓冲技术。这意味着每个窗口会有两个渲染缓冲区,一个前置缓冲区和一个后置缓冲区。 190 // 前置缓冲区会在屏幕上显示而后置缓冲区是你渲染的目标。 191 // 当整个帧已经渲染完毕时,两个缓冲区需要进行交换,所以后置缓冲区会变成前置缓冲区,反之亦然。 192 glfwSwapBuffers(window); 193 // 处理窗口相关事件 194 glfwPollEvents(); 195 } 196 // 销毁窗口 197 glfwDestroyWindow(window); 198 // 终止运行 199 glfwTerminate(); 200 201 exit(EXIT_SUCCESS); 202 }