如何为着色器devise一个简单的GLSL包装器

更新:因为我马上需要一些东西,所以我创build了一个简单的着色器包装器,它可以处理我需要的东西。 你可以在这里find它: GitHub上的ShaderManager 。 请注意,它是为Objective-C / iOSdevise的,因此可能对每个人都没用。 如果您有任何关于devise改进的build议,请告诉我!

原始问题:

我是使用GLSL着色器的新手。 我对GLSL语言和OpenGL接口非常熟悉,但是在devise使用着色器的简单API时遇到了麻烦。

OpenGL的C接口与着色器交互看起来很麻烦。 我似乎无法在网上find任何涵盖这些APIdevise的教程。

我的问题是: 任何人都有一个好的,简单的APIdevise或模式来包装OpenGL着色器程序API?

以下面的简单例子。 假设我有一个模拟固定function的顶点着色器和两个片段着色器 – 一个用于绘制光滑的矩形,一个用于绘制光滑的圆。 我有以下文件:

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 

或者,我可以创build一个大的程序,片段着色器检查一些有条件的统一variables,然后调用着色器函数来生成结果。 例如:

 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); 

在我想绘制每种types的元素之前。 然后,我需要在使用之前设置每个程序的制服(模型/视图/投影)和属性。 这似乎很笨拙。

使用单个ShaderProgram选项,我仍然需要在片段着色器中设置某种布尔开关(圆形或方形),然后绘制每个像素。 这似乎也很复杂。

作为一个方面说明,我允许将两个片段着色器(每个都带有main()函数)链接到一个着色器程序中吗? OpenGL如何知道要调用哪一个?

设置variables

电话:

 glUniform* glVertexAttribPointer 

用于在当前程序中设置制服和属性指针位置。

不同的类和结构可能需要从代码中的不同位置访问和设置当前着色器(或更改当前着色器)上的variables。 我想不出一个很好的方法来做到这一点,从想要使用它的代码中分离出着色器代码。

也就是说,我想要绘制的每个形状都需要设置顶点和纹理坐标属性 – 要求由OpenGL生成的这些属性的句柄。

相机将需要在顶点着色器中将其投影matrix设置为统一,而pipe理模型matrix堆栈的类将需要在顶点着色器中设置自己的统一。

通过绘制场景来改变着色器意味着所有这些类将需要再次设置其制服和属性。

大多数人如何devise这个?

一个由句柄或名字访问的着色器的全局字典,它们的参数是getter和setter?

带着色器对象的OOdevise,每个都有参数?

我看了下面的包装器:

乔恩的茶壶:GLSL着色器pipe理器 – 这包装着C ++类中的着色器。 看起来只不过是一个在C API上强制执行OO原则的包装,导致C ++ API大致相同。

我经过任何简化了Shader程序devise的devise,并不关心使用的特定范例(OO,程序等)

基本链接:

这里没有标准的方法。 至less有两种一般的方法:

  1. 单片 – 一个着色器覆盖了很多情况,使用统一的布尔开关。 这些分支不会损害性能,因为条件结果对于任何片段组(实际上,对于所有片段)是恒定的。

  2. 多对象程序合成 – 主着色器声明一组入口点(如“get_diffuse”,“get_specular”等),这些入口点是在独立的着色器对象中实现的。 这意味着每个对象的独立着色器,但是任何caching都有帮助。

设置variables:制服

我将只描述我开发的方法。

每个着色器程序都有一个统一的字典列表。 它用于在程序(重新)链接时填充统一的源列表。 当程序被激活时,它通过统一的列表,从他们的来源获取值并上传到GL。 结果是,数据不直接与用户着色器程序相关联,不pipe怎样pipe理它都不关心使用它的程序。

例如,这些字典中的一个可以是核心字典,包含模型,视图转换,相机投影以及可能的其他字典。

设置variables:属性

首先,着色程序是一个属性消费者,所以必须从网格(或任何其他数据存储)中提取这些属性,并以需要的方式将其上传到GL。 还应该确保提供的属性types与请求的types匹配。

当使用单片着色器方法时,当禁用的分支方式需要未提供的顶点属性时,可能会出现不愉快的情况。 我会build议使用另一个属性的数据来​​提供缺失的,因为我们不关心在这种情况下的实际值。

PS您可以在这里find这些想法的实际实现: http : //code.google.com/p/kri/

我看到这个标签是用iOS标记的,所以如果你偏爱Objective-C,我会仔细看看Jeff LaMarche的GLProgram包装类,他在这里描述了这个类,并且在这里有源代码。 我已经在自己的应用程序中使用它来简化一些着色器程序设置,并使代码更清洁一些。

例如,您可以使用如下所示的代码设置着色器及其属性和制服:

 sphereDepthProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"SphereDepth" fragmentShaderFilename:@"SphereDepth"]; [sphereDepthProgram addAttribute:@"position"]; [sphereDepthProgram addAttribute:@"inputImpostorSpaceCoordinate"]; if (![sphereDepthProgram link]) { NSLog(@"Depth shader link failed"); NSString *progLog = [sphereDepthProgram programLog]; NSLog(@"Program Log: %@", progLog); NSString *fragLog = [sphereDepthProgram fragmentShaderLog]; NSLog(@"Frag Log: %@", fragLog); NSString *vertLog = [sphereDepthProgram vertexShaderLog]; NSLog(@"Vert Log: %@", vertLog); [sphereDepthProgram release]; sphereDepthProgram = nil; } sphereDepthPositionAttribute = [sphereDepthProgram attributeIndex:@"position"]; sphereDepthImpostorSpaceAttribute = [sphereDepthProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; sphereDepthModelViewMatrix = [sphereDepthProgram uniformIndex:@"modelViewProjMatrix"]; sphereDepthRadius = [sphereDepthProgram uniformIndex:@"sphereRadius"]; 

当你需要使用着色器程序时,你可以做如下的事情:

 [sphereDepthProgram use]; 

这并没有解决上面提到的分支与单个着色器的问题,但是Jeff的实现确实提供了一些OpenGL ES样板着色器设置代码的很好的封装。