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