原始问题:
我刚刚使用GLSL着色器.我熟悉GLSL语言和OpenGL界面,但是我无法设计一个使用着色器的简单API.
OpenGL的C界面与着色器交互似乎很麻烦.我似乎找不到任何涵盖API设计的网络上的教程.
我的问题是:有没有任何一个好的,简单的API设计或模式来包装OpenGL着色器程序API?
以下简单的例子.说我有一个顶点着色器只是模拟固定的功能和两个片段着色器 – 一个用于绘制光滑的矩形,一个用于绘制光滑的圆.我有以下文件:
Shader.vsh : Simple vertex shader,with the following inputs/outputs: -- Uniforms: mat4 Model,mat4 View,mat4 Projection -- Attributes: vec4 Vertex,vec2 TexCoord,vec4 Color -- Varying: vec4 vColor,vec2 vTexCoord Square.fsh : Fragment shader for drawing squares based on tex coord / color Circle.fsh : Fragment shader for drawing circles based on tex coord / color
基本链接
现在使用这些标准的方法是什么?将上述着色器链接到两个OpenGL着色器程序?那是:
Shader.vsh + Square.fsh = SquareProgram Shader.vsh + Circle.fsh = CircleProgram
或者,我可以创建一个大程序,其中片段着色器检查一些条件均匀变量,并调用一个着色器函数来生成其结果.例如:
Shader.vsh + Square.fsh + Circle.fsh + Main.fsh = ShaderProgram //Main.fsh here would simply check whether to call out to square or circle
有两个单独的程序,我可能需要打电话
glUseProgram(CircleProgram); or glUseProgram(SquareProgram);
在每种类型的元素之前,我想绘制.在使用之前,我需要设置制服(模型/视图/投影)和每个程序的属性.这似乎太笨了
使用单一的ShaderProgram选项,我仍然需要在片段着色器中设置一些布尔开关(圆形或正方形),在绘制每个像素之前要进行检查.这也看起来很复杂.
作为一个附注,我允许将两个片段着色器(每个具有一个main()函数)链接到一个着色器程序中? OpenGL如何知道要调用哪一个?
设置变量
电话:
glUniform* glVertexAttribPointer
用于在当前程序中设置制服和属性指针位置.
不同的类和结构可能需要从代码中的不同位置访问和设置当前着色器上的变量(或更改当前着色器).我不能想到一个很好的方式来实现,使着色器代码与要使用它的代码分离.
也就是说,我想要绘制的每个形状都需要设置顶点和纹理坐标属性 – 需要对由OpenGL生成的那些属性的句柄.
相机将需要将其投影矩阵设置为顶点着色器中的统一,而管理模型矩阵堆栈的类将需要在顶点着色器中设置自己的均匀度.
通过绘制场景改变着色器将意味着所有这些类都需要重新设置它们的制服和属性.
这个大多数人怎么设计呢?
一个由句柄或名称访问的着色器全局字典,其参数为getter和setter?
一个OO设计与着色器对象每个都有参数?
我看了下面的包装:
Jon’s Teapot: GLSL Shader Manager – 它将着色器包装在C类中.它似乎只是一个比C API执行OO原则的包装器,导致一个C API是一样的.
我经过任何设计,简化了Shader程序的使用,并不关心所使用的特定范例(OO,程序等)
解决方法
>单片 – 一个着色器涵盖许多情况,使用均匀的布尔开关.这些分支不会影响性能,因为条件结果对于任何片段组(实际上对于所有片段)而言是不变的.
>多目标程序合成 – 主着色器声明一组入口点(如“get_diffuse”,“get_specular”等),它们在附加的单独的着色器对象中实现.这意味着每个对象的单独着色器,但任何类型的缓存都有帮助.
设置变量:制服
我将只描述我开发的方法.
每个着色器程序都有一个统一的字典列表.它用于在程序(重新)链接时填写统一的源列表.当程序激活时,它将通过统一列表,从其来源获取值并将其上传到GL.在结果中,数据与用户着色器程序没有直接关联,无论管理什么都不关心使用它的程序.
其中一个词典可以是例如核心词典,其中包含模型,视图变换,相机投影,也可以是别的内容.
设置变量:属性
首先,着色器程序是一个属性消费者,因此,必须从网格(或任何其他数据存储)中提取这些属性,并以需要的方式将其上传到GL.它还应该确保提供的属性的类型与请求的类型匹配.
当使用单片着色器方法时,当一个禁用的分支路径需要未提供的顶点属性时,可能会出现不愉快的情况.我会建议使用另一个属性的数据来提供丢失的数据,因为我们不关心这种情况下的实际值.
附:
您可以在这里找到这些想法的实际实现:http://code.google.com/p/kri/