OpenGl学习记录
今天主要学习了如何去写shader,了解了GLSL语言的一些语法
参考文档:LearnOpenGL https://learnopengl-cn.github.io/
关于GLSL:
- 向量类型
类型 | 含义 |
---|---|
vecn | 包含n 个float分量的默认向量 |
bvecn | 包含n 个bool分量的向量 |
ivecn | 包含n 个int分量的向量 |
uvecn | 包含n 个unsigned int分量的向量 |
dvecn | 包含n 个double分量的向量 |
在使用时,我们可以通过类似vec.x、vec.y这种操作来获取它的分量,并且允许向量作为参数传给不同的向量构造函数
vec3 myVec = vec3(0.5, 0.5, 0.5);
vec4 otherVec = vec4(myVec, 1.0);
还有另一种重组的语法
vec3 myVec = vec3(0.5, 0.5, 0.5);
vec2 newVec = vec2(0.3, 0.3)
vec4 otherVec = vec3.zyxx + vec2.xyxy;
如上所示,我们可以随意的将一个向量的分量赋予到另一个向量的分量上,并且可以进行运算操作
- 输入输出
GLSL中会运用 in 和 out 关键字来定义输入输出
在片元着色器中,我们可以直接将顶点着色器的输出用作输入
而在顶点着色器中,我们是以一种特殊的形式接收数据的,所以需要加入 layout (location = 0) 这样的标识符,这个标识符定义了我们在获取数据的时候应该在哪一个位置获取
如下面所示的例子:
float vertices[] = {
// 位置 // 颜色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
};
这个顶点数据不仅含有位置信息,而且含有颜色信息,这时我们在接收数据时需要运用位置标识符将两组数据分开存储
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
同时,我们需要更新顶点的格式,保证我们在运行shader时获取的数据是对的
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);
- uniform
uniform是全局变量,在渲染过程的任何阶段都可以被访问,如下面代码所示,我们定义了一个uniform变量,并将它作为片元着色器颜色渲染的输出值
#version 330 core
out vec4 FragColor;
uniform vec4 ourColor;
void main()
{
FragColor = ourColor;
}
这时我们想要访问这个变量,对它进行动态修改,需进行如下操作
//获取到ourColor变量的位置
int ourColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
//通过 glUniform 函数设置uniform的值
glUniform4f(ourColorLocation, 0.0f, 1.0f, 0.0f, 1.0f);
封装一个着色器类:
class Shader
{
public:
//shader程序的id
unsigned int ID;
Shader(const char* vertexPath, const char* fragmentPath);
//激活程序
void use();
//uniform工具函数
void setBool(const string& name, bool value) const;
void setInt(const string& name, int value) const;
void setFloat(const string& name, float value) const;
};
定义好了着色器类以后,我们开始实现里面的构造函数和成员函数
- 构造函数
Shader::Shader(const char* vertexPath, const char* fragmentPath) {
string vertexCode;
string fragmentCode;
ifstream vShaderFile;
ifstream fShaderFile;
vShaderFile.exceptions(ifstream::failbit | ifstream::badbit);
fShaderFile.exceptions(ifstream::failbit | ifstream::badbit);
try {
//打开文件
vShaderFile.open(vertexPath, ios::in);
fShaderFile.open(fragmentPath, ios::in);
//读取内容到数据流
stringstream vShaderStream, fShaderStream;
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
//关闭文件
vShaderFile.close();
fShaderFile.close();
//将数据流转换成string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
unsigned int vertexShader,fragmentShader;
int success;
char infoLog[512];
//顶点着色器
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vShaderCode, NULL);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl;
}
//片元着色器
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fShaderCode, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl;
}
//着色器程序
ID = glCreateProgram();
glAttachShader(ID, vertexShader);
glAttachShader(ID, fragmentShader);
glLinkProgram(ID);
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(ID, 512, NULL, infoLog);
cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl;
}
//删除着色器
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
- 成员函数
void Shader::use() {
glUseProgram(ID);
}
void Shader::setBool(const string& name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void Shader::setInt(const string& name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::setFloat(const string& name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
- 着色器类的使用
当上面的一切准备好之后,我们只需要传入顶点着色器和片元着色器就可以使用了
//创建一个VAO对象,将之后的顶点属性储存在这个VAO中
unsigned int VAO;
//构建顶点数据
draw(VAO);
//使用构造函数构造Shader
Shader myShader("vertexShader.vs", "fragmentShader.fs");
//渲染循环
while (!glfwWindowShouldClose(window)) {
...
//渲染部分
myShader.use();
...
}