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

发布于 2024-10-15 15:30:05 字数 2427 浏览 1 评论 0原文

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

每个网格(好吧,子网格)都有许多顶点流(例如位置、法线、纹理、切线等)到一个大 VBO 和一个 MaterialID 中。

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

然后我有一个 GLSL 着色器,及其制服和属性。比方说:

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

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

对网格物体说的话:“将你的位置流放入属性“Position”中,并将你的纹理坐标放入“TexCoord”中。还将材质的漫反射颜色放入“DiffuseColor”中,将材质的第二个纹理放入“NormalMapTexture”

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

。创建“顶点声明”,但也包括制服和纹理

所以我只是想知道人们如何在大型渲染引擎中执行此操作

编辑:

给出了属性/统一语义。通过变量的名称 (我现在在做什么) 对每个可能的属性使用预定义的名称。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 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 绑定器将循环遍历属性并绑定到字典中找到(或没有?)的

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

属性:与制服相同,仅查询活动材质和全局变量还有。

I'm coding a small rendering engine with GLSL shaders:

Each Mesh (well, submesh) has a number of vertex streams (eg. position,normal,texture,tangent,etc) into one big VBO and a MaterialID.

Each Material has a set of textures and properties (eg. specular-color, diffuse-color, color-texture, normal-map,etc)

Then I have a GLSL shader, with it's uniforms and attributes. Let's say:

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

I'm a little bit stuck in trying to design a way for the GLSL shader to define the stream mappings (semantics) for the attributes and uniforms, and then bind the vertex streams to the appropriate attributes.

Something in the lines of saying to the mesh :"put your position stream in attribute "Position" and your tex coordinates in "TexCoord". Also put your material's diffuse color in "DiffuseColor" and your material's second texture in "NormalMapTexture"

At the moment I am using hard-coded names for the attributes (ie. vertex pos is always "Position" ,etc) and checking each uniform and attribute name to understand what the shader is using it for.

I guess I'm looking for some way of creating a "vertex declaration", but including uniforms and textures too.

So I'm just wondering how people do this in large-scale rendering engines.

Edit:

Recap of suggested methods:

1. Attribute/Uniform semantic is given by the name of the variable
(what I'm doing now)
Using pre-defined names for each possible attribute.The GLSL binder will query the name for each attribute and link the vertex array based on the name of the variable:

//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. Predefined locations for each semantic

GLSL binder will always bind the vertex arrays to the same locations.It is up to the shader to use the the appropriate names to match. (This seems awfully similar to method 1, but unless I misunderstood, this implies binding ALL available vertex data, even if the shader does not consume it)

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

3. Dictionary of available attributes from Material, Engine globals, Renderer and Mesh

Maintain list of availlable attributes published by the active Material, the Engine globals, the current Renderer and the current Scene Node.

eg:

 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}

then in shader:

 uniform vec3 ambientColor,diffuseColo;
 attribute vec3 Position;

When binding the vertex data to the shader, the GLSL binder will loop over the attribs and bind to the one found (or not? ) in the dictionary:

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

and the same with uniforms, only query active Material and globals aswell.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

贪恋 2024-10-22 15:30:05

属性:

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

连接后,您可以查询 GLSL 程序的活动属性并形成该程序的属性字典。这里的每个元素只是(名称,类型)。

当您使用指定的GLSL程序绘制网格时,您会遍历程序属性字典并绑定相应的网格流(或者在不一致的情况下报告错误)。

统一:

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

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

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

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

  • 加载器填写网格数据流声明和材质字典。
  • 渲染使用知道名称的着色器,提供附加参数并选择要绘制的正确网格。

Attributes:

Your mesh has a number of data streams. For each stream you can keep the following info: (name, type, data).

Upon linking, you can query the GLSL program for active attributes and form an attribute dictionary for this program. Each element here is just (name, type).

When you draw a mesh with a specified GLSL program, you go through programs attribute dictionary and bind the corresponding mesh streams (or reporting an error in case of inconsistency).

Uniforms:

Let the shader parameter dictionary be the set of (name, type, data link). Typically, you can have the following dictionaries:

  • Material (diffuse,specular,shininess,etc) - taken from the material
  • Engine (camera, model, lights, timers, etc) - taken from engine singleton (global)
  • Render (custom parameters related to the shader creator: SSAO radius, blur amount, etc) - provided exclusively by the shader creator class (render)

After linking, the GLSL program is given a set of parameter dictionaries in order to populate it's own dictionary with the following element format: (location, type, data link). This population is done by querying the list of active uniforms and matching (name, type) pair with the one in dictionaries.

Conclusion:
This method allows for any custom vertex attributes and shader uniforms to be passed, without hard-coded names/semantics in the engine. Basically only the loader and render know about particular semantics:

  • Loader fills out the mesh data streams declarations and materials dictionaries.
  • Render uses a shader that is aware of the names, provides additional parameters and selects proper meshes to be drawn with.
厌倦 2024-10-22 15:30:05

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

您所能做的就是定义自己的方式将您的语义映射到 OpenGL 变量,使用您可以控制这些变量的唯一参数:它们的位置。

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

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

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

  • 查询着色器的活动属性
  • 解析着色器源代码以自行查找它们

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

无论如何,对于您的活动属性名称,您必须为它们分配位置(和/或语义),为此,您只能处理您的用例。

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

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

之后,由着色器编写器来遵守属性的预定义语义。

AFAIK这是最常见的做事方式,OGRE 使用它作为例子。这不是火箭科学,但在实践中效果很好。

如果您想添加一些控制,您可以提供一个 API 来定义着色器基础上的语义,甚至可能在附加文件中包含此描述,易于解析,位于着色器源代码附近。

我不会讨论情况几乎相同的统一,除了“较新”的扩展允许您强制 GLSL 统一块到与您的应用程序兼容的内存布局。

我自己对这一切并不满意,所以我很高兴获得一些相互矛盾的信息:)

From my experience, OpenGL does not define the concept of attributes or uniforms semantics.

All you can do is define your own way of mapping your semantics to OpenGL variables, using the only parameter you can control about these variables: their location.

If you're not constrained by platform issues, you could try to use the 'new' GL_ARB_explicit_attrib_location (core in OpenGL 3.3 if I'm not mistaken) that allows shaders to explicitly express which location is intended for which attribute. This way, you can hardcode (or configure) which data you want to bind on which attribute location, and query the shaders' locations after it's compiled. It seems that this feature is not yet mature, and perhaps subject to bugs in various drivers.

The other way around is to bind the locations of your attributes using glBindAttribLocation. For this, you have to know the names of the attributes that you want to bind, and the locations you want to assign them.

To find out the names used in a shader, you can:

  • query the shader for active attributes
  • parse the shader source code to find them yourself

I would not recommend using the GLSL parsing way (although it may suit your needs if you're in simple enough contexts): the parser can easily be defeated by the preprocessor. Supposing that your shader code becomes somewhat complex, you may want to start using #includes, #defines, #ifdef, etc. Robust parsing supposes that you have a robust preprocessor, which can become quite a heavy lift to set up.

Anyway, with your active attributes names, you have to assign them locations (and/or semantics), for this, you're alone with your use case.

In our engine, we happily hardcode locations of predefined names to specific values, such as:

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

After that, it's up to the shader writer to conform to the predefined semantics of the attributes.

AFAIK it's the most common way of doing things, OGRE uses it for example. It's not rocket science but works well in practice.

If you want to add some control, you could provide an API to define the semantics on a shader basis, perhaps even having this description in an additional file, easily parsable, living near the shader source code.

I don't get into uniforms where the situation is almost the same, except that the 'newer' extensions allow you to force GLSL uniform blocks to a memory layout that is compatible with your application.

I'm not satisfied by all this myself, so I'll be happy to have some contradictory information :)

内心旳酸楚 2024-10-22 15:30:05

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

统一/属性声明语法非常简单。您可以设计一个小型手动解析器,查找以 uniformattribute 开头的行,获取类型和名称,然后使用字符串公开一些 C++ API。这将为您省去硬编码名称的麻烦。如果你不想手动解析,那么Spirit之类的就可以了诀窍。
您可能不想完全解析 GLSL,因此您需要确保在减速过程中没有做任何可能改变实际含义的有趣事情。我想到的一个复杂问题是在 GLSL 中使用宏进行条件编译。

You may want to consider actually parsing the GLSL itself.

The uniform/attribute declaration syntax is pretty simple. You can come up with a small manual parser that looks for lines that start with uniform or attribute, get the type and name and then expose some C++ API using strings. This will save you the trouble of hard coded names. If you don't want to get your hands dirty with manual parsing a couple of likes of Spirit would do the trick.
You probably won't want to fully parse GLSL so you'll need to make sure you don't do anything funny in the decelerations that might alter the actual meaning. One complication that comes to mind is conditional compilation using macros in the GLSL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文