场景动画

  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 }

 

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