着色器
着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。
着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
一个典型的GLSL编写的着色器有下面结构。
#version version_number
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
int main()
{
// 处理输入并进行一些图形操作
...
// 输出处理过的结果到输出变量
out_variable_name = weird_stuff_we_processed;
}
当谈到顶点着色器时,每个输入变量也叫做顶点属性,我们能声明的顶点属性是有上限的,由硬件来决定。
数据类型
基础类型
GLSL中包含C等其它语言大部分的默认基础数据类型:int
、float
、double
、uint
和bool
。
GLSL也有两种容器类型,它们会在这个教程中使用很多,分别是向量(Vector)和矩阵(Matrix)
向量
GLSL中的向量是一个可以包含由1、2、3、4个分量的容器,分量的类型可以是前面默认基础类型中的其中一个。
类型 | 含义 |
---|---|
vecn | 包含n 个float分量的默认向量 |
bvecn | 包含n 个bool分量的向量 |
ivecn | 包含n 个int分量的向量 |
uvecn | 包含n 个unsigned int分量的向量 |
dvecn | 包含n 个double分量的向量 |
向量的4个分量可以用.x
, .y
, .z
和.w
来获取。
向量也允许一种灵活的分量选择方式,称为重组。
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
可以把向量当成一个参数传给不同的向量的构造函数,以减少参数的数量。
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
输入输出
每个着色器通过输入、输出来进行相互的数据交流和传递。输入输出变量的使用in
和out
来标识。每个着色器使用这两个关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。
顶点着色器可以从从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location
来指定输入变量,以便在CPU上配置顶点属性。
片段着色器需要一个vec4
的颜色输出变量。
所以,如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了。
Uniform
Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。
uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。
无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。
查询uniform地址不要求你之前使用过着色器程序,但是更新一个uniform之前你必须先使用程序,因为它是在当前激活的着色器程序中设置uniform的。