基于Qt的OpenGL可编程管线学习(4)- 使用Subroutine绘制不同光照的模型
在上一篇文章中,我们介绍了如何在OpenGL的可编程管线中使用函数指针绘制不同光照的模型。本文中,我们将介绍另一种实现方式——使用Subroutine。
Subroutine是OpenGL 4.0中引入的一个概念,它可以看作是类似于函数的代码段,但与函数不同的是,Subroutine可以在运行时动态地决定具体调用哪个代码段。这意味着我们可以使用Subroutine来实现根据不同光照绘制模型的功能。
在使用Subroutine之前,我们需要先定义Subroutine。定义Subroutine的方式如下:
#version 420
subroutine void lightingModel();
subroutine(lightingModel)
void diffuse() {...}
subroutine(lightingModel)
void specular() {...}
...
上述代码定义了一个Subroutine类型lightingModel,该类型包含了两个具体实现:diffuse和specular。在定义具体实现时需要使用subroutine(lightingModel)修饰符来指定实现属于哪个Subroutine类型。
接下来,我们需要在渲染过程中使用Subroutine。具体实现如下:
// 定义Subroutine类型
subroutine void lightingModel();
subroutine(lightingModel) void diffuse();
subroutine(lightingModel) void specular();
// 将uniform变量设置为Subroutine类型
layout(location = 0) uniform subroutine uniform lightingModelUniform;
// 在主函数中根据不同的光照选择具体的Subroutine实现
void main()
{
...
// 设置Subroutine类型
subroutine(lightingModel) lightModel = diffuse;
glUseProgram(...);
glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &lightModel);
...
}
在上述代码中,我们定义了一个Subroutine uniform变量lightingModelUniform,并在主函数中根据需要选择具体的Subroutine实现。在选择具体实现时,我们可以使用glUniformSubroutinesuiv函数来指定Subroutine uniform变量的值,从而选择相应的Subroutine实现。
除了使用glUniformSubroutinesuiv函数之外,我们还可以使用另一种方式来选择Subroutine实现。具体实现如下:
// 定义Subroutine类型
subroutine void lightingModel();
subroutine(lightingModel) void diffuse();
subroutine(lightingModel) void specular();
// 将Subroutine实现映射到具体的编号
layout(location = 0) uniform subroutine uniform lightingModelUniform;
layout(binding = 0) uniform subroutine uniform lightingModelFunctions { lightingModel diffuse specular ... };
// 在主函数中使用具体的编号选择Subroutine实现
void main()
{
...
// 设置Subroutine类型
glUniform1ui(lightingModelUniform, 1);
...
}
在上述代码中,我们使用了一个新的uniform变量lightingModelFunctions,将Subroutine实现映射到具体的编号,并使用glUniform1ui函数来选择相应的编号(此处选择了diffuse实现)。
需要注意的是,Subroutine uniform变量必须使用subroutine uniform关键字进行修饰。此外,在GLSL中,Subroutine只能定义在顶层作用域中,不能放在函数中。
本文中,我们介绍了如何使用Subroutine来实现根据不同光照绘制模型的功能。与使用函数指针相比,使用Subroutine可以让代码更加清晰易懂。需要注意的是,在使用Subroutine时需要定义Subroutine类型,并将uniform变量设置为Subroutine类型,同时需要在渲染过程中选择具体的Subroutine实现。
