GLSL中的顶点着色器属性映射

时间:2011-02-02 11:03:21

标签: opengl glsl rendering-engine

我正在使用GLSL着色器编写一个小型渲染引擎:

每个网格(井,子网格)都有一些顶点流(例如位置,法线,纹理,切线等)到一个大的VBO和一个MaterialID中。

每种材质都有一组纹理和属性(例如镜面反射颜色,漫反射颜色,颜色纹理,法线贴图等)

然后我有一个GLSL着色器,它的制服和属性。让我们说:

uniform vec3 DiffuseColor;
uniform sampler2D NormalMapTexture;
attribute vec3 Position;
attribute vec2 TexCoord;

我试图为GLSL着色器设计一种方法来定义属性和制服的流映射(语义),然后将顶点流绑定到适当的属性,我有点困惑。

对网格说法的内容:“将您的位置流放在属性”位置“中,将您的tex坐标放在”TexCoord“中。还将材质的漫反射颜色放在”DiffuseColor“中,将材质的第二个纹理放在”NormalMapTexture“中“

目前我正在为属性使用硬编码名称(即顶点pos始终是“位置”等)并检查每个统一和属性名称以了解着色器使用它的位置。

我想我正在寻找创建“顶点声明”的方法,但也包括制服和纹理。

所以我只是想知道人们如何在大型渲染引擎中做到这一点。

编辑:

回顾建议的方法:

1。属性/统一语义由变量名称给出 (我现在在做什么) 为每个可能的属性使用预定义的名称.GLSL绑定器将查询每个属性的名称,并根据变量的名称链接顶点数组:

//global static variable

semantics (name,normalize,offset) = {"Position",false,0} {"Normal",true,1},{"TextureUV,false,2}

 ...when linking
for (int index=0;index<allAttribs;index++)
{
   glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);      
   semantics[index]= GetSemanticsFromGlobalHardCodedList(name);
} 
... when binding vertex arrays for render
 for (int index=0;index<allAttribs;index++)
{
    glVertexAttribPointer(index,size[index],type[index],semantics[index]->normalized,bufferStride,semantics[index]->offset);

}  

2。每个语义的预定义位置

GLSL活页夹总是将顶点数组绑定到相同的位置。着色器需要使用相应的名称进行匹配。 (这似乎与方法1非常类似,但除非我误解,否则这意味着绑定所有可用的顶点数据,即使着色器不使用它)

.. when linking the program...
glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");

第3。 Material,Engine globals,Renderer和Mesh

中可用属性的字典

维护活动材质,引擎全局,当前渲染器和当前场景节点发布的可用属性列表。

例如:

 Material has (uniformName,value) =  {"ambientColor", (1.0,1.0,1.0)}, {"diffuseColor",(0.2,0.2,0.2)}
 Mesh has (attributeName,offset) = {"Position",0,},{"Normals",1},{"BumpBlendUV",2}

然后在着色器中:

 uniform vec3 ambientColor,diffuseColo;
 attribute vec3 Position;

当将顶点数据绑定到着色器时,GLSL绑定器将遍历attrib并绑定到字典中找到的(或不是?):

 for (int index=0;index<allAttribs;index++)
    {
       glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);      
      semantics[index] = Mesh->GetAttributeSemantics(name);
}

和制服一样,只查询活动材料和全局变量。

3 个答案:

答案 0 :(得分:16)

<强>属性:

您的网格有许多数据流。对于每个流,您可以保留以下信息:(名称,类型,数据)。

链接后,您可以在GLSL程序中查询活动属性并为该程序形成属性字典。这里的每个元素都是( name,type )。

当您使用指定的GLSL程序绘制网格时,您将浏览程序属性字典并绑定相应的网格流(或在出现不一致时报告错误)。

<强>制服:

让着色器参数字典为(名称,类型,数据链接)的集合。通常,您可以使用以下词典:

  • 材料(漫反射,镜面反射,光泽等) - 取自材料
  • 引擎(相机,模型,灯光,计时器等) - 取自引擎单件(全局)
  • 渲染(与着色器创建者相关的自定义参数:SSAO半径,模糊量等) - 由着色器创建者类(渲染)专门提供

链接之后,GLSL程序被赋予一组参数字典,以便使用以下元素格式填充它自己的字典:(位置,类型,数据链接)。通过查询活动制服列表并匹配(名称,类型)对与词典中的一对来完成此填充。

<强>结论: 此方法允许传递任何自定义顶点属性和着色器制服,而不会在引擎中使用硬编码名称/语义。基本上只有加载器和渲染器知道特定的语义:

  • Loader填写网格数据流声明和材料词典。
  • 渲染使用了解名称的着色器,提供其他参数并选择要绘制的正确网格。

答案 1 :(得分:8)

根据我的经验,OpenGL没有定义属性或制服语义的概念。

您所能做的就是定义自己的方法将您的语义映射到OpenGL变量,使用您可以控制的唯一参数来控制这些变量: location

如果您不受平台问题的限制,您可以尝试使用“新的”GL_ARB_explicit_attrib_location(OpenGL 3.3中的核心,如果我没有记错的话),它允许着色器明确表示哪个位置适用于哪个属性。这样,您可以硬编码(或配置)要绑定到哪个属性位置的数据,并在编译后查询着色器的位置。看来这个功能尚未成熟,可能会遇到各种驱动程序中的错误。

另一种方法是使用 glBindAttribLocation 绑定属性的位置。为此,您必须知道要绑定的属性的名称以及要为其分配的位置。

要找出着色器中使用的名称,您可以:

  • 在着色器中查询活动属性
  • 解析着色器源代码以自行查找

我不建议使用GLSL解析方式(尽管如果你的上下文足够简单,它可能适合你的需要):解析器很容易被预处理器击败。假设您的着色器代码变得有些复杂,您可能希望开始使用#includes,#define,#ifdef等。强大的解析假设您有一个强大的预处理器,这可能会成为一个非常重要的设置。

无论如何,使用您的活动属性名称,您必须为它们分配位置(和/或语义),为此,您将独自使用您的用例。

在我们的引擎中,我们很乐意将预定义名称的位置硬编码为特定值,例如:

glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");
...

之后,着色器编写器必须符合属性的预定义语义。

AFAIK这是最常用的做事方式,OGRE以此为例。这不是火箭科学,但在实践中运作良好。

如果你想添加一些控件,你可以提供一个API来在着色器的基础上定义语义,甚至可以将这个描述放在一个附加文件中,可以很容易地解析,生活在着色器源代码附近。

除了“更新”的扩展允许您将GLSL统一块强制应用于与您的应用程序兼容的内存布局之外,我不会进入情况几乎相同的制服。

我自己并不满足于此,所以我很乐意提供一些相互矛盾的信息:)

答案 2 :(得分:3)

您可能需要考虑实际解析GLSL本身。

统一/属性声明语法非常简单。您可以使用一个小型手动解析器来查找以uniformattribute开头的行,获取类型和名称,然后使用字符串公开一些C ++ API。这将为您节省硬编码名称的麻烦。如果您不想亲自动手解析,可以使用Spirit之类的几个人来解决这个问题。
您可能不希望完全解析GLSL,因此您需要确保在减速中不做任何有趣的事情,这可能会改变实际意义。想到的一个复杂因素是使用GLSL中的宏进行条件编译。