如果OpenGL(或Direct3D!)允许您在每个顶点/片段/任何阶段拥有多个着色器,那将是非常好的,但不幸的是我们被困在现有系统中。
假设您编写了一堆GLSL函数。其中一些是通用于所有对象的,例如应用模型视图变换和将纹理坐标复制到下一个阶段。有些则特定于特定类别的对象,例如水或岩石。
然后您编写的是“ubershader”,这是一个程序,在其中顶点/片段/任何阶段的main()函数除了调用所有这些函数之外什么都不做。这是一个模板或原型,从中生成更专业的程序。
最常见的方法是使用预处理器和大量的#ifdefs来调用main()函数内部的函数调用。也许如果您没有任何#define编译,您会得到标准变换和Gouraud着色。添加#define WATER以获得水效果,#define DISTORT以获得某种自由形变算法,两者都可以获得自由形变的水,#define FOG以添加雾效果,...
您甚至不需要拥有超级着色器源代码的多个副本,因为您可以在运行时生成#define字符串,并将它们传递给glCompileShader。
您最终得到了很多着色器程序,每种渲染类型一个。如果出于任何原因,您希望始终只使用一个程序,则可以在较新的GLSL子例程系统上执行类似操作。
这些基本上是GLSL中的函数指针,您可以像uniforms一样设置它们。现在您的ubershader在main()函数中有1,2,...个函数指针调用。您的程序只需将#1设置为标准变换,#2设置为岩石/水/其他,#3设置为雾,...如果您不想使用阶段,请拥有一个NOP函数,可以分配它。
虽然这具有仅使用一个程序的优点,但与#define方法相比并不那么灵活,因为任何给定指针都必须使用相同的函数原型。如果说WATER需要在多个着色器中处理,那么这也需要更多的工作,因为您必须记住在每个着色器中设置函数指针,而不仅仅是单个#define。
希望这可以帮助您。
main()
函数即可。 - Reto Koradi