1. 着色器编译
1.1 着色器编译的命令序列

1.2 加载着色器步骤
对于每个着色器对象 :
- 创建一个着色器对象;
- 将着色器源代码编译为对象;
- 验证着色器的编译是否成功。
然后需要将多个着色器对象链接为一个着色器程序,包括 :
- 创建一个着色器程序;
- 将着色器对象关联到着色器程序;
- 链接着色器程序;
- 判断着色器的链接过程是否成功完成;
- 使用着色器来处理顶点和片元。
2. 着色器子程序
2.1 函数的动态调用
如果需要动态地选择调用不同的函数:
- 可以创建两个不同的着色器;
- 或者使用
if 语句来进行运行时的选择,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #version 330 core
void func_1() { ... } void func_2() { ... }
uniform int func;
void main() ( if (func == 1) func_1(); else func_2(); )
|
- 着色器子程序在概念上类似于C语言中的函数指针,它可以实现动态子程序选择过程。
- 在着色器当中,可以预先声明一个可用子程序的集合,然后动态地指定子程序的类型。
- 然后,通过设置一个子程序的
uniform 变量,从预设的子程序中选择一个并加以执行。
2.2 GLSL 的子程序设置
当我们需要在着色器中进行子程序的选择时,通常需要三个步骤来设置一个子程序池。
2.2.1 声明子程序
通过关键字 subroutine 来定义子程序的类型:
1
| subroutine returnType subroutineType(type param, ...);
|
returnType 可以是任何类型的函数返回值;
subroutineType 是一个合法的子程序名称;
- 由于它相当于函数的原型,因此我们只需要给出参数的类型,不一定给出参数的名称(可以将它设想为C语言中的
typedef ,而 subroutineType 就是新定义的类型)。
2.2.2 定义子程序
使用刚才定义的 subroutineType ,通过 subroutine 关键字来定义这个子程序集合的内容,以便稍后进行动态的选择。某个子程序函数的原型定义类似于下面的形式:
1
| subroutine (subroutineType) returnType functionName(...);
|
指定一个子程序 uniform 变量,其中保存了相当于 “函数指针” 的子程序选择信息,这可以在应用程序中更改:
1
| subroutine uniform subroutineType variableName;
|
2.2.4 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| subroutine vec4 LightFunc(vec3);
subroutine (LightFunc) vec4 ambient(vec3 n) { return Materials.ambient; }
subroutine (LightFunc) vec4 diffuse(vec3 n) { return Materials.diffuse * max(dot(normalize(n), LightVec.xyz), 0.0); }
subroutine uniform LightFunc materialShader;
|
2.2.5 多类型子程序
子程序并不一定只属于一个子程序类型,如果定义了多种类型的子程序,那么我们可以设置一个子程序属于多个类型,方法是在定义子函数时把类型添加到列表中:
1 2 3 4 5 6 7 8 9 10
| subroutine void Type_1(); subroutine void Type_2(); subroutine void Type_3();
subroutine (Type_1, Type_2) Func_1 (); subroutine (Type_1, Type_3) Func_2 ();
subroutine uniform Type_1 myFunc_1; subroutine uniform Type_2 myFunc_2; subroutine uniform Type_3 myFunc_3;
|
Note :
myFunc_1 可以使用 Func_1() 和 Func_2() ,因为这两个子程序都指定了 Type_1 ;
myFunc_2 只能使用 Func_1() ;
myFunc_3 只能使用 Func_2() 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| GLint materialShaderLoc; GLuint ambientIndex; GLuint diffuseIndex;
glUseProgram(program);
materialShaderLoc = glGetSubroutineUniformLocation ( program, GL_VERTEX_SHADER, "materialShader");
if (materialShaderLoc < 0) { }
ambientIndex = glGetSubroutinelndex(program, GL_VERTEX_SHADER, "ambient" ); diffuseIndex = glGetSubroutinelndex(program, GL_VERTEX_SHADER, "diffuse" );
if (ambientlndex == GL_INVALID_INDEX || diffuseindex == GL_INVALID_INDEX) { } else {
GLsizei n; glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &n);
GLuint * indices = new GLuint[n]; indices[materialShaderLoc] = ambientIndex;
glUniformSubroutinesuiv(GL_VERTEX_SHADER, n, indices);
delete[] indices; }
|
3. 独立的着色器对象
当需要使用多个片元着色器来处理来自同一个顶点着色器的几何体变换数据时,可以使用 独立的着色对象 ,将不同程序的着色阶段(例如顶点着色)合并到同一个程序管线中。
// TODO