GLSL - 将语义绑定到语义

时间:2013-04-05 05:53:05

标签: opengl attributes glsl shader semantics

我在这里看到了关于这个主题的堆栈溢出的一些问题和答案。从这些答案中,我提出了将GLSL属性绑定到用户定义的语义的可能解决方案。我想得到一些意见和讨论,并检查它是否是一个有效的想法。

首先,让我们假设我们有一些用户定义的语义列表:

enum VertexElementSemantic
{
  POSITION, NORMAL, AMBIENT, DIFFUSE, SPECULAR,
  TEX_COORD0, TEX_COORD1, TEX_COORD2, TEX_COORD3,
  INDICES
};

一种封装设置顶点属性指针所需数据的结构。

struct VertexElement
{
  unsigned int m_source;
  unsigned int m_offset;
  unsigned int m_stride;
}

现在,一些RenderOperation类将包含VertexElementSemantics到VertexElements的映射。 VertexElement的规范化格式,大小和格式可由其语义决定。

为了设置这个指针,我们需要的最后一点信息是属性位置本身。这是我们想要将VertexElementSemantic绑定到特定位置的地方。

the first answer to this question,我们了解到我们可以明确说明每个属性的所需位置,如下所示:

layout(location = 0) in vec3 position;

因此我们可以将我们的语义映射到这些硬编码位置,但是我们要求在每个着色器中对此位置进行硬编码。对这些位置的任何更改都需要我们浏览并编辑每个着色器。

但是,此值不必由着色器源提供。从the answer to this question开始,我们了解到我们可以将#defines外部添加到我们的着色器中,如下所示:

char *sources[2] = { "#define FOO\n", sourceFromFile };
glShaderSourceARB(shader, 2, sources, NULL);

使用这个,我们可以构建一个字符串,#string定义每个语义所需位置的变量。例如,我们可以构建一个字符串,最终将以下内容插入到每个着色器的开头:

#define POSITION_LOCATION 0
#define NORMAL_LOCATION 1
#define AMBIENT_LOCATION 2
...

回过头来明确说明我们的属性位置,我们现在应该可以这样说明:

layout(location = POSITION_LOCATION) in vec3 position;
layout(location = NORMAL_LOCATION) in vec3 normal;
layout(location = AMBIENT_LOCATION) in vec4 ambient;

此方法允许我们在代码中设置每个Semantic的所需属性位置。它还为着色器本身提供了一种语义绑定感觉。像这样的系统是否朝着正确的方向迈出了一步,以解决为属性位置提供意义的问题?

1 个答案:

答案 0 :(得分:4)

让我们考虑一下这个想法的后果。

  

我们可以构建一个字符串,#string定义每个语义所需位置的变量。例如,我们可以构建一个字符串,最终将以下内容插入到每个着色器的开头:

嗯,这有两个方面很糟糕。首先,问题是#version。如果您要使用 任何 除1.10之外的GLSL版本,则必须提供#version声明。并且该声明必须在着色器中的第一件事,在评论和空白之外。

通过将这些#define放入您的着色器源(无论是通过字符串连接,还是使用多个字符串),您必须接受某些后果。通常,每个单独的着色器文件都有自己的#version声明,指定它使用的GLSL版本。但是如果你想使用除GLSL 1.10之外的其他东西,你就无法做到这一点。您必须在#version之前生成#define的C ++源代码。

这意味着您的着色器源现在与其编译的版本解耦。这是可行的,但这意味着您的着色器源现在不清楚,而不知道它是什么版本。您可以通过其他方式传达版本,例如使用文件名(例如,lit_transform_330.vert将使用版本3.30)。但是你必须设计这样一个系统。

现在已经解决了版本问题,问到下一个问题:你正在做的是冗余

您使用" semantic"等术语,这些术语对OpenGL没有任何意义。您似乎正在尝试将某种形式的名称分配给特定的顶点属性,以便您可以在着色器和C ++代码中看到该名称的使用,从而知道它的属性。

也就是说,你想在" name"之间定义一个映射。和"属性索引"。您希望它在一个地方定义,以便它自动传播到每个着色器并在整个C ++源代码中一致地使用。

我们已经在名称和属性索引之间建立了映射。它被称为"属性名称和属性索引之间的映射"。每个着色器必须为其属性提供名称。这是您在in vec4 position;等定义中看到的字符串名称,该属性的名称为position。这就是GLSL在使用变量时调用变量的原因。

如您所链接的答案中所述,您可以在链接程序之前将特定属性名称与C ++代码中的属性索引相关联。这是通过the glBindAttribLocation function完成的。您可以设置任意数量的映射。链接程序时,将为该位置分配与指定位置匹配的属性。

您需要的只是一个"语义列表" (aka:属性索引)和字符串名称,您需要着色器用于这些属性。

你可能会说,"嗯,我希望着色器能够随心所欲地调用变量。"我的回答是......差异是什么?您建议的方案已要求用户遵守特定的命名约定。只是他们必须使用的名称不是变量的名称;它是在声明时与变量关联的某个标记的名称。

那究竟有什么区别?着色器的编写者必须遵守顶点属性变量名的集合命名方案?不是所有着色器中同一个概念的一致名称​​好东西吗?

唯一的区别是,如果他们错误地输入了#34;语义"在你的方案下,他们得到一个着色器编译错误(因为他们错误的"语义"名字赢得了与任何实际的#define匹配)。然而,如果他们错误地输入属性的名称,如果他们在使用该属性时不会错误输入该名称,他们将只会遇到编译器错误。

有办法解决这个问题。它需要使用program introspection来遍历活动属性列表,并根据期望的属性名称进行检查。

你可以将它归结为一组非常简单的约定。使用你的"语义"定义:

enum VertexElementSemantic
{
  POSITION, NORMAL, AMBIENT, DIFFUSE, SPECULAR,
  TEX_COORD0, TEX_COORD1, TEX_COORD2, TEX_COORD3,
  INDICES, NUM_SEMANTICS
};

//in the C++ file you use to link your shaders
const char *AttributeNames[] =
{
  "position", "normal", "ambient", "diffuse", "specular", 
  "tex_coord0", "tex_coord1", "tex_coord2", "tex_coord3", 
  "indices",
}

static_assert(ARRAY_COUNT(AttributeNames) == NUM_SEMANTICS); //Where `ARRAY_COUNT` is a macro that computes the number of elements in a static array.

GLuint CreateProgram(GLuint vertexShader, GLuint fragmentShader)
{
  GLuint prog = glCreateProgram();
  //Attach shaders
  for(int attrib = 0; attrib < NUM_SEMANTICS; ++attrib)
  {
    glBindAttribLocation(prog, attrib, AttributeNames[attrib]);
  }

  glLinkProgram(prog);

  //Detach shaders
  //Check for linking errors

  //Verify that attribute locations are as expected.
  //Left as an exercise for the reader.

  return prog;
}

就个人而言,我只会使用一个数字。无论你使用什么,编写着色器的人都必须遵守一些约定。这意味着当他们去写一个占据位置的顶点着色器时,他们将不得不查找如何说&#34;这是一个位置&#34;。所以无论如何,他们都必须在某个地方的桌子上看

在这一点上,它归结为最可能的问题。最可能的问题可能是认为他们知道答案但实际上是错误的人(即:没有查找),以及错误输入答案的人。错误输入一个数字真的很难(虽然肯定会发生),而错误输入POSITION_LOCATION会容易得多。前一个问题可能发生在一个或多或少相同的数字中。

所以在我看来,如果您的约定基于数字而不是单词,那么您更有可能减少常规错配问题。