GSGL
着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。
着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
一个典型的着色器有下面的结构:
#version version_number
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
void main()
{
// 处理输入并进行一些图形操作
...
// 输出处理过的结果到输出变量
out_variable_name = weird_stuff_we_processed;
}
数据类型
GSGL中有两种容器类型向量
Vector
和矩阵Matrix
,这里先讨论向量类型向量
GLSL中的向量是一个可以包含有2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(
n
代表分量的数量):
类型 | 含义 |
---|---|
vecn |
包含n 个float分量的默认向量 |
bvecn |
包含n 个bool分量的向量 |
ivecn |
包含n 个int分量的向量 |
uvecn |
包含n 个unsigned int分量的向量 |
dvecn |
包含n 个double分量的向量 |
输入与输出
GSGL 通过定义
in
和out
关键字实现每个着色器的输入与输出。只要输出变量与下一个着色器的输入变量相匹配,流水线就会传递下去。
其中,顶点着色器接受的是顶点数据的输入而非任意着色器的输出。我们使用location
这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。layout (location = 0)
顶点着色器需要为它的输入提供一个额外的layout
标识,这样我们才能把它链接到顶点数据。
同样,片段着色器的输出需要一个vec4
的颜色输出变量。
顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为0
out vec4 vertexColor; // 为片段着色器指定一个颜色输出
void main()
{
gl_Position = vec4(aPos, 1.0); // 注意我们如何把一个vec3作为vec4的构造器的参数
vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 把输出变量设置为暗红色
}
片段着色器
#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)
void main()
{
FragColor = vertexColor;
}
Uniform
Uniform类型类似于我们常用的全局变量,但是他的作用范围并不仅仅局限于一个代码文件中。他在整个着色器程序(Program)中是独一无二的,同时可以被着色器程序任意阶段所访问。
要使用Uniform 首先需要在GLSL程序中定义它(==任意着色器均可==)uniform vec4 ourColor; // 在OpenGL程序代码中设定这个变量
这里我们希望在我们的C程序中实时修改它,因此需要在主循环中获取它的索引并更新它。
float timeValue = glfwGetTime();
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
注意到glUniform
这个方法 由于opengl是C编写的 因此没有重载。入参变化后只能通过另一个函数实现
因为OpenGL在其核心是一个C库,所以它不支持类型重载,在函数参数不同的时候就要为其定义新的函数;glUniform是一个典型例子。这个函数有一个特定的后缀,标识设定的uniform的类型。可能的后缀有:
后缀 | 含义 |
---|---|
f |
函数需要一个float作为它的值 |
i |
函数需要一个int作为它的值 |
ui |
函数需要一个unsigned int作为它的值 |
3f |
函数需要3个float作为它的值 |
fv |
函数需要一个float向量/数组作为它的值 |
更多属性!
之前我们的vertices
数组仅有顶点属性,在这里我们可以把颜色属性也添加进去
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 // 顶部
};
调整了顶点数组后,同时我们要调整顶点着色器的入参,能够接收这样的顶点属性输入
#version 330 core
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
out vec3 ourColor; // 向片段着色器输出一个颜色
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor; // 将ourColor设置为我们从顶点数据那里得到的输入颜色
}