读取GLSL文件

2004年,2.0版本中引入了OpenGL着色语言(GLSL),使得“着色器程序”可以在图形管线的各个阶段被安装和直接执行。GLSL作为一种着色器语言,主要运行于GPU上。虽然说着色器代码可以硬编码在C++程序的字符串中,但是对于大型程序,这无疑是一种灾难,所以有必要学习怎么从文件读取GLSL源码。

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

 

vertShader.glsl源码:

1 #version 430  
2 
3 void main(void)    
4 { 
5     gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
6 }

 

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:46  禅元天道  阅读(660)  评论(0编辑  收藏  举报