GLSL-使用Subroutines选择着色器功能

http://blog.csdn.net/xiajun07061225/article/details/7664336

 

 

在GLSL中,subroutine是这样一种机制:它根据某个变量的值把一个函数调用和一个函数定义集合中的某一个函数定义绑定起来。在很多方面它和C语言中的函数指针类似。一个全局变量充当指针,用来调用函数。可以在OpenGL中设置这个变量的值,这样可以把它和多个函数定义中的一个绑定起来。subroutine中的函数定义不一定要同名,但是必须要有同等数量和相同类型的参数以及相同的返回类型

Subroutines提供了在运行时刻不用切换着色器或者是重新编译或者是使用if判断就能选择不同实现功能的方法。例如,一个着色器里面可以实现多种算法用来在场景中渲染不同的对象。当渲染场景的时候,我们可以通过改变subroutines的全局变量的值来选择合适的渲染算法而不是去通过切换着色器或者是使用条件判断来实现。

注意:由于性能在着色器中越来越重要,所以避免条件判断或着色器切换是很有必要的。有了subroutines,我们就能够在不增加计算负载的情况下实现条件判断和着色器切换的功能。


下面用一个例子来进行说明。在这个例子中,采用subroutine渲染了两次茶壶。分别采用不同的光照模型。第一次用ADS(ambient,diffuse,specular)模型进行渲染,第二次只用diffuse模型渲染。一个subroutine全局变量用来选择渲染算法。下面两幅图可以看出效果的对比。

GLSL-使用Subroutines选择着色器功能_第1张图片

左边是ADS模型渲染效果,右边是diffuse模型渲染效果。


下面介绍使用subroutines的步骤。

我们在vertex shader中进行subroutines的定义。

(1)首先是定义subroutine类型:

subroutine vec3 shadeModelType( vec4 position, vec3 normal);

上面的代码定义了一个新的subroutine类型,其名字是shadeModelType。其语意和函数原型很类似。它定义了一个名字,一个参数列表和返回类型,参数名不是必须的。

(2)紧接着我们定义一个上述类型的uniform变量shadeModel:

subroutine uniform shadeModelType shadeModel;

这个变量充当了函数指针的作用,它在OpenGL应用程序中将会被赋值为两个可能的函数之一。

(3)定义两个函数,作为subroutine的一部分。

它们都有一个前缀是:

subroutine( shadeModelType )

其意味着函数和subroutine类型匹配。我们使用这个前缀定义了两个函数:

[cpp] view plain copy print ?
  1. subroutine( shadeModelType )  
  2. vec3 phongModel( vec4 position, vec3 norm )  
  3. {  
  4.     vec3 s = normalize(vec3(Light.Position - position));  
  5.     vec3 v = normalize(-position.xyz);  
  6.     vec3 r = reflect( -s, norm );  
  7.     vec3 ambient = Light.La * Material.Ka;  
  8.     float sDotN = max( dot(s,norm), 0.0 );  
  9.     vec3 diffuse = Light.Ld * Material.Kd * sDotN;  
  10.     vec3 spec = vec3(0.0);  
  11.     if( sDotN > 0.0 )  
  12.         spec = Light.Ls * Material.Ks *  
  13.                pow( max( dot(r,v), 0.0 ), Material.Shininess );  
  14.   
  15.     return ambient + diffuse + spec;  
  16. }  
  17.   
  18. subroutine( shadeModelType )  
  19. vec3 diffuseOnly( vec4 position, vec3 norm )  
  20. {  
  21.     vec3 s = normalize( vec3(Light.Position - position) );  
  22.     return  
  23.         Light.Ld * Material.Kd * max( dot(s, norm), 0.0 );  
  24. }  
(4)函数调用

在vertex shader的main函数中,我们使用subroutine的uniform变量shadeModel来调用定义的两个函数。

LightIntensity = shadeModel( eyePosition, eyeNorm );

这样,根据uniform变量shadeModel的值,这个调用会被绑定到上述我们定义的两个函数之一。

(5)给subroutine uniform变量赋值。

分两步进行。

1、使用glGetSubroutineIndex函数查询每个subroutine函数的索引。

[cpp] view plain copy print ?
  1. GLuint adsIndex = glGetSubroutineIndex( programHandle,  
  2.         GL_VERTEX_SHADER,  
  3.         "phongModel" );  
  4. GLuint diffuseIndex = glGetSubroutineIndex(programHandle,  
  5.         GL_VERTEX_SHADER,  
  6.         "diffuseOnly");  
第一个参数是程序的句柄。第二个是着色器。第三个是subroutine的名字。查询到的索引存储在两个GLuint型的值中。
2、为了选择合适的subroutine函数,我们需要给subroutine uniform变量shadeModel赋值。采用如下函数调用实现:

glUniformSubroutinesuiv( GL_VERTEX_SHADER, 1, &adsIndex);

这个函数可以一次设置多个uniform变量值。第一个参数指定着色阶段。第二个是要设定的uniform变量的数量。这里只需要设置一个uniform变量的值。

如果要设置多个,那么第三个参数应该是个数组,通常数组中的第i个元素赋值给subroutine uniform变量中索引为i的那个。

这里只需要设置一个值,我们设置的是索引为0 的那么subroutine uniform变量。

注意:OpenGL中,uniform 变量的索引默认就是从0开始的。如果有多个变量需要设置,可以用 glGetSubroutineUniformLocation()查询。 

接下来进行渲染就可以得出相应的效果了。


要进行另外一种效果的渲染只需调用下面函数:

glUniformSubroutinesuiv( GL_VERTEX_SHADER, 1, &diffuseIndex);

再进来渲染过程即可。

你可能感兴趣的:(算法,存储,语言,float,shader,subroutine)