检测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 }

 

posted @ 2021-09-07 14:28  禅元天道  阅读(433)  评论(0编辑  收藏  举报