《OpenGL编程指南》一2.6 着色器子程序

简介:

本节书摘来自华章出版社《OpenGL编程指南》一书中的第2章,第2.6节,作者 Bill Licea-Kane ,更多章节内容可以访问云栖社区“华章计算机”公众号查看

2.6 着色器子程序

高级技巧
GLSL允许我们在着色器中定义函数,而这些函数的调用过程总是静态的。如果需要动态地选择调用不同的函数,那么可以创建两个不同的着色器,或者使用if语句来进行运行时的选择,如例2.5所示。
例2.5 静态着色器的控制流程
image

着色器子程序在概念上类似于C语言中的函数指针,它可以实现动态子程序选择过程。在着色器当中,可以预先声明一个可用子程序的集合,然后动态地指定子程序的类型。然后,通过设置一个子程序的uniform变量,从预设的子程序中选择一个并加以执行。

2.6.1 GLSL的子程序设置

当我们需要在着色器中进行子程序的选择时,通常需要三个步骤来设置一个子程序池。
1)通过关键字subroutine来定义子程序的类型:
image

其中returnType可以是任何类型的函数返回值,而subroutineType是一个合法的子程序名称。由于它相当于函数的原型,因此我们只需要给出参数的类型,不一定给出参数的名称(我们可以将它设想为C语言中的typedef,而subroutineType就是新定义的类型)。
2)使用刚才定义的subroutineType,通过subroutine关键字来定义这个子程序集合的内容,以便稍后进行动态的选择。某个子程序函数的原型定义类似于下面的形式:
image

3)最后,指定一个子程序uniform变量,其中保存了相当于“函数指针”的子程序选择信息,这可以在应用程序中更改:
image

将上面的三个步骤整合在一起,我们可以通过例2.6来实现环境光照和漫反射光照方式的动态选择。
例2.6 声明一个子程序集合
image
image

子程序并不一定只属于一个子程序类型(例如,例2.6中的LightFunc)。如果定义了多种类型的子程序,那么我们可以设置一个子程序属于多个类型,方法是在定义子函数时把类型添加到列表中,如下所示:
image

在上面的例子中,func_1可以使用Func_1和Func_2,这是因为两个子程序都指定了Type_1。但是,func_2就只能使用Func_1,而func_3只能使用Func_2。

2.6.2 选择着色器子程序

如果我们已经在着色器中定义了所有子程序类型和函数,那么只需要在链接后的着色器程序中查询一些数值,然后使用这些数值来选择合适的函数即可。
在之前所示的步骤3当中,我们声明了一个子程序的uniform变量,之后就可以获取它的位置并设置它的值。与其他的uniform变量不同的是,子程序的uniform需要使用glGetSubroutineUniformLocation()来获取自身的位置。
GLint glGetSubroutineUniformLocation(GLuint program, GLenum shadertype, const char* name);
返回名为name的子程序uniform的位置,相应的着色阶段通过shadertype来指定。name是一个以NULL结尾的字符串,而shadertype的值必须是GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_GEOMETRY_SHADER或者GL_FRAGMENT_SHADER中的一个。
如果name不是一个激活的子程序uniform,则返回–1。如果program不是一个可用的着色器程序,那么会生成一个GL_INVALID_OPERATION错误。
当取得了子程序uniform数值之后,我们需要判断某个子程序在着色器中的索引号。这一步可以通过调用函数glGetSubroutineIndex()来完成。
GLuint glGetSubroutineIndex(GLuint program, GLenum shadertype, const char* name);
从程序program中返回name所对应的着色器函数的索引,相应的着色阶段通过shadertype来指定。name是一个以NULL结尾的字符串,而shadertype的值必须是GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_GEOMETRY_SHADER或者GL_FRAGMENT_SHADER中的一个。
如果name不是shadertype着色器的一个活动子程序,那么会返回GGL_INVALID_INDEX。
当我们得到了子程序的索引以及uniform的位置之后,可以使用glUniformSubroutinesuiv()来指定在着色器中执行哪一个子程序函数。某个着色阶段中,所有的子程序uniform都必须先经过初始化的过程。
GLuint glUniformSubroutinesuiv(GLenum shadertype, GLsizei count, const GLuint* indices);
设置所有count个着色器子程序uniform使用indices数组中的值,相应的着色阶段通过shadertype来指定。shadertype的值必须是GL_VERTEX_SHADER、GL_TESS_CONTROL_SHADER、GL_TESS_EVALUATION_SHADER、GL_GEOMETRY_SHADER或者GL_FRAGMENT_SHADER中的一个。第i个子程序uniform对应于indices[i]的值。
如果count不等于当前绑定程序的着色阶段shadertype的GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS值,那么会产生一个GL_INVALID_VALUE错误。indices中的所有值都必须小于GL_ACTIVE_SUBROUTINES,否则会产生一个GL_INVALID_VALUE错误。
将上面的步骤组合在一起,可以得到下面的代码段,它演示了例2.6中的顶点着色器的调用过程。
image
image

 调用glUseProgram()时会重新设置所有子程序uniform的值,具体的顺序与硬件实现相关。

相关文章
|
6月前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
123 0
|
数据可视化
QT+OpenGL几何着色器
输入布局限定符可以从顶点着色器接收下列任何一个图元值: ● points:绘制GL_POINTS图元时 ● lines:绘制GL_LINES或GL_LINE_STRIP时 ● lines_adjacency:GL_ADJACENCY或GL_LINESTRIP_ADJACENCY ● triangles:GL_TRIANGLES、GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN ● triangles_adjacency:GL_TRIANGLES_ADJACENCY或GL_TRIANGLE_STRIP_ADJACENCY
117 0
|
缓存 图形学
《OpenGL编程指南(原书第9版)》——导读
OpenGL图形系统是图形硬件的一种软件接口(GL表示Graphics Library,即图形库)。它使得用户可以创建交互式的程序以产生运动的三维对象的颜色图像。通过OpenGL,我们可以使用计算机图形学技术产生逼真的图像,或者通过一些虚构的方式产生虚拟的图像。
3002 0
|
缓存 索引
OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
|
编译器 C语言 C++
OpenGL学习笔记(十一):封装自己的着色器类
OpenGL学习笔记(十一):封装自己的着色器类
OpenGL学习笔记(十一):封装自己的着色器类
|
存储 缓存 API
三、OpenGL ES GLSL语言 & 自定义着色器常用API
OpenGL ES GLSL语言 & 自定义着色器常用API
314 0
三、OpenGL ES GLSL语言 & 自定义着色器常用API
|
存储
了解OpenGL中的投影方式、存储着色器分类、常见图元连接方式
了解OpenGL中的投影方式、存储着色器分类、常见图元连接方式
238 0
了解OpenGL中的投影方式、存储着色器分类、常见图元连接方式
|
存储 芯片 异构计算