GLSL中的顶点着色器属性映射
我正在用GLSL着色器编码一个小的渲染引擎:
每个Mesh(well,submesh)都有一个顶点stream(例如,position,normal,texture,tangent等)到一个大的VBO和一个MaterialID中。
每种材质都有一组纹理和属性(例如镜面颜色,漫reflection颜色,颜色纹理,法线贴图等)
然后我有一个GLSL着色器,它是制服和属性。 我们说:
uniform vec3 DiffuseColor; uniform sampler2D NormalMapTexture; attribute vec3 Position; attribute vec2 TexCoord;
我试图为GLSL着色器devise一种方法来定义属性和制服的stream映射(语义),然后将顶点stream绑定到适当的属性。
对网格说:“把你的位置stream属性”位置“和你的tex坐标在”TexCoord“。也把你的材料的漫reflection颜色在”DiffuseColor“和你的材料的第二个纹理”NormalMapTexture“
目前,我正在使用硬编码名称的属性(即顶点pos始终是“位置”等),并检查每个统一和属性名称,以了解着色器正在使用它。
我想我正在寻找创build“顶点声明”的方法,但也包括制服和纹理。
所以我只是想知道人们是如何在大型渲染引擎中这样做的。
编辑:
build议的方法回顾:
1.属性/统一语义由variables的名称给出 (我现在在做什么)为每个可能的属性使用预定义的名称.GLSL活页夹将查询每个属性的名称,并根据variables名称:
//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.“材质”,“引擎全局”,“渲染器”和“网格”中的可用属性字典
维护活动材质,引擎全局variables,当前渲染器和当前场景节点发布的可用属性的列表。
例如:
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活页夹将遍历attribs并绑定到字典中find(或不是?)的那个:
for (int index=0;index<allAttribs;index++) { glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name); semantics[index] = Mesh->GetAttributeSemantics(name); }
和统一一样,只查询活动的材质和全局。
属性:
您的网格有许多数据stream。 对于每个stream,你可以保留以下信息:( 名称,types,数据 )。
链接后,您可以查询GLSL程序的活动属性,并为此程序形成一个属性字典。 这里的每个元素只是( 名称,types )。
当您使用指定的GLSL程序绘制网格物体时,您将通过程序属性字典并绑定相应的网格物体stream(或者在出现不一致的情况下报告错误)。
制服:
让着色器参数字典为( 名称,types,数据链接 )的集合。 通常,你可以有以下字典:
- 材料(漫reflection,镜面reflection,光泽等) – 取自材料
- 引擎(相机,模型,灯光,定时器等) – 从单发(全局)
- 渲染(与着色器创build者相关的自定义参数:SSAO半径,模糊量等) – 由着色器创build者类(渲染)专门提供,
链接之后,GLSL程序被赋予一组参数字典,以便使用以下元素格式填充它自己的字典:( 位置,types,数据链接 )。 这个人口是通过查询活动制服列表和字典匹配( 名称,types )对来完成的。
结论:这个方法允许传递任何自定义的顶点属性和着色器制服,而引擎中没有硬编码的名称/语义。 基本上只有加载器和渲染知道特定的语义:
- Loader填充网格数据stream声明和材料字典。
- 渲染使用着名的着色器,提供额外的参数,并select适当的网格来绘制。
根据我的经验,OpenGL没有定义属性或制服语义的概念。
你所能做的就是定义你自己的映射你的语义到OpenGLvariables的方法,使用你可以控制的variables的唯一参数:它们的位置 。
如果你不受平台问题的限制,你可以尝试使用' new'GL_ARB_explicit_attrib_location (如果我没有弄错的话,OpenGL 3.3中的核心)允许着色器明确地表示哪个位置是用于哪个属性的。 这样,您可以硬编码(或configuration)哪些数据要绑定在哪个属性位置,并在编译后查询着色器的位置。 看来这个function还不成熟,也许会受到各种驱动程序的bug影响。
另一种方法是使用glBindAttribLocation来绑定你的属性的位置。 为此,您必须知道要绑定的属性的名称以及要分配的位置。
要找出着色器中使用的名称,您可以:
- 查询着色器的活动属性
- parsing着色器源代码来自己find它们
我不推荐使用GLSLparsing方式(尽pipe如果你的脚本足够简单,它可能适合你的需求):parsing器很容易被预处理器击败。 假设你的着色器代码变得有点复杂,你可能想要开始使用#includes,#defines,#ifdef等等。强大的parsing假设你有一个强大的预处理器,这可能会变得相当沉重。
无论如何,你的活动属性名称,你必须分配他们的位置(和/或语义),为此,你一个人用你的用例。
在我们的引擎中,我们高兴地将预定义名称的位置硬编码为特定的值,例如:
glBindAttribLocation(prog, 0, "mg_Position"); glBindAttribLocation(prog, 1, "mg_Color"); glBindAttribLocation(prog, 2, "mg_Normal"); ...
之后,由着色器作者决定是否符合预定义的属性语义。
AFAIK这是最常见的做事方式, OGRE使用它。 这不是火箭科学,但在实践中运作良好。
如果你想添加一些控制,你可以提供一个API来在着色器的基础上定义语义,甚至可以把这个描述放在一个额外的文件中,很容易parsing,靠近着色器的源代码。
除了“更新的”扩展允许您强制GLSL统一块到与您的应用程序兼容的内存布局之外,我不会进入制服的情况几乎相同。
我不满意这一切,所以我会很高兴有一些矛盾的信息:)
你可能要考虑实际parsingGLSL本身。
统一/属性声明的语法非常简单。 你可以想出一个小的手动parsing器,查找以uniform
或attribute
开头的行,获取types和名称,然后使用string公开一些C ++ API。 这将为您节省硬编码名称的麻烦。 如果你不想手动parsing你的手,那么Spirit的一些喜欢就可以做到。
你可能不想完全parsingGLSL,所以你需要确保你在减速过程中不要做任何有趣的事情,这可能会改变实际的意义。 想到的一个复杂情况是使用GLSL中的macros进行条件编译。