OpenGL 着色器详解
1. GLSL语言
glsl语言是用来编写着色器的,通过一段一段包含main函数的程序片段,告诉渲染引擎怎么去渲染内容。
glsl语言的语法有点类似c语言风格,只是增加了一些特有的关键字来修饰变量,下面是一个着色器基本的程序结构:
首先声明的是GLSL的版本号和模式,然后就是声明变量。像其他语言一样,声明变量我们要包括数据类型和变量名两个部分。而在GLSL中,我们还要指定变量的类型。
in
,通过in
关键字来声明,现在声明的变量是一个输入变量。out
,对应的out
关键字表明这是一个输出变量。uniform
,而uniform
就比较厉害了,他代表生成的变量是一个全局变量。
最后一部分就是主函数main
。我们在这里处理输入输出值。
2. 输入变量in
首先我们可以定义的输入变量的个数是有限的,这一般取决于硬件,但OpenGL至少还是会为我们保留16个输入变量可用。我们可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限:
3. 输出变量out
由于片段着色器直接输出色值交个显卡渲染,下一个阶段不会对色值进行处理,所以我们生成的输出变量无需与下一个阶段产生联系。但是试想一下,如果我们在顶点着色器中,想传递一个参数给片段着色器怎么办呢?**我们要建立起输入变量和输出变量间的关系。**怎么建立呢,GLSL给了我们一个很简单的方式来建立联系,一致的数据类型及变量名称即可。
顶点着色器
片段着色器
4 . 全局变量
全局的作用范围作用于整个着色器程序上。也就是你把带有全局变量x的着色器源码编译在着色器A上,再把A添加到着色器程序B上,那么B中的所有着色器都可以使用变量x。并且在GL编码中,也可以通过对应的着色器程序获取到指定的全局变量x。
这里我们仅提供一个在片段着色器中使用全局变量的示例:
片段着色器
渲染循环
5. 数据类型
向量
向量重组
这是一个设置顶点变量属性的函数,通过它可以设置步长,偏移量和位置
顶点数据
顶点着色器
片段着色器
我们看到,顶点着色器中,我们声明了两个输入变量。一个代表位置,一个代表颜色
。然后我们要把颜色值传给片段着色器,所以要声明一个输出变量,在片段着色器中在声明一个以一样的输入变量,如此,颜色也就从顶点着色器传到片段着色器中了。
着色器配置完成,我们还要设置顶点数据的属性。
7. Shader Class
一个着色器程序就是一个最小的绘制单元。其绘制结果直接取决于两个着色器。基于面向对象的思想,我们希望对其进行封装。
我们来梳理一下加载着色器的代码:
创建着色器对象->附加着色器源码并编译->创建着色器程序->绑定着色器->链接着色器
基于上述过程,我们封装了一下着色器程序的类:
1 #ifndef Shader_hpp 2 #define Shader_hpp 3 4 #include <glad/glad.h> 5 #include <GLFW/glfw3.h> 6 #include <string> 7 #include <fstream> 8 #include <sstream> 9 #include <iostream> 10 11 class Shader { 12 public: 13 ///着色器程序ID 14 unsigned int ID; 15 ///构造器读取着色器 16 Shader(const GLchar * vertexPath,const GLchar * fragmentPath) { 17 std::string vertexCode; 18 std::string fragmentCode; 19 std::ifstream vShaderFile; 20 std::ifstream fShaderFile; 21 // ensure ifstream objects can throw exceptions: 22 vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); 23 fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); 24 try 25 { 26 ///打开文件 27 vShaderFile.open(vertexPath); 28 fShaderFile.open(fragmentPath); 29 std::stringstream vShaderStream, fShaderStream; 30 ///读取文件 31 vShaderStream << vShaderFile.rdbuf(); 32 fShaderStream << fShaderFile.rdbuf(); 33 ///关闭文件 34 vShaderFile.close(); 35 fShaderFile.close(); 36 ///获取源码 37 vertexCode = vShaderStream.str(); 38 fragmentCode = fShaderStream.str(); 39 } 40 catch (std::ifstream::failure e) 41 { 42 std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; 43 } 44 ///获取c字符串 45 const char* vShaderCode = vertexCode.c_str(); 46 const char * fShaderCode = fragmentCode.c_str(); 47 ///编译 48 unsigned int vertex, fragment; 49 ///顶点着色器 50 vertex = glCreateShader(GL_VERTEX_SHADER); 51 glShaderSource(vertex, 1, &vShaderCode, NULL); 52 glCompileShader(vertex); 53 checkCompileErrors(vertex, "VERTEX"); 54 ///片段着色器 55 fragment = glCreateShader(GL_FRAGMENT_SHADER); 56 glShaderSource(fragment, 1, &fShaderCode, NULL); 57 glCompileShader(fragment); 58 checkCompileErrors(fragment, "FRAGMENT"); 59 ///着色器程序 60 ID = glCreateProgram(); 61 glAttachShader(ID, vertex); 62 glAttachShader(ID, fragment); 63 glLinkProgram(ID); 64 checkCompileErrors(ID, "PROGRAM"); 65 ///释放着色器 66 glDeleteShader(vertex); 67 glDeleteShader(fragment); 68 } 69 70 ///激活主色器程序 71 void use() { 72 glUseProgram(ID); 73 } 74 75 ///uniform setter 76 void setBool(const std::string &name ,GLboolean value) const { 77 glUniform1i(glGetUniformLocation(ID,name.c_str()),(GLint)value); 78 } 79 void setInt(const std::string &name ,GLint value) const { 80 glUniform1i(glGetUniformLocation(ID,name.c_str()),value); 81 } 82 void setFloat(const std::string &name ,GLfloat value) const { 83 glUniform1f(glGetUniformLocation(ID,name.c_str()),value); 84 } 85 void setVec4f(const std::string &name ,GLfloat x,GLfloat y,GLfloat z,GLfloat w) const { 86 glUniform4f(glGetUniformLocation(ID,name.c_str()),x,y,z,w); 87 } 88 89 private: 90 ///检查编译错误 91 void checkCompileErrors(unsigned int shader, std::string type) 92 { 93 int success; 94 char infoLog[1024]; 95 if (type != "PROGRAM") 96 { 97 glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 98 if (!success) 99 { 100 glGetShaderInfoLog(shader, 1024, NULL, infoLog); 101 std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; 102 } 103 } 104 else 105 { 106 glGetProgramiv(shader, GL_LINK_STATUS, &success); 107 if (!success) 108 { 109 glGetProgramInfoLog(shader, 1024, NULL, infoLog); 110 std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; 111 } 112 } 113 } 114 }; 115 116 #endif /* Shader_hpp */