为什么要为一个属性指定不同的顶点格式

Why specify different vertex formats for one attribute?

本文关键字:格式 顶点 属性 一个 为什么      更新时间:2023-10-16

OpenGL 函数glVertexAttribPointerglVertexAttribFormat允许用户指定渲染时将绑定到着色器程序中给定属性变量的数据格式。 顶点属性格式是诸如数据类型(intfloatbyte等(、属性变量中的维数(vec2vec3等(、数据是否应该规范化以及数据数组起始位置的顶点数组偏移量等信息。 这些函数指定构建顶点数组对象 (VAO( 时的格式,并且指定的格式是 VAO 状态的一部分。 所以这是我的问题:

为什么要与属性关联的数据的格式是 VAO 状态的一部分,而不是属性状态的一部分? 换句话说,为什么数据的格式与VAO而不是属性相关联? 在什么情况下,我的 VAO 会对同一属性使用不同的格式?

为了更清楚起见,这里有一个例子,应该说明为什么我感到困惑。 想象一下,在我的顶点着色器中,我声明了变量:

in vec3 position;

现在我在我的 OpenGL 应用程序中获取属性位置:

GLint positionAttribute = glGetAttribLocation(myProgram, "position");

现在,当我创建一个 VAO 时,我指定的数据格式如下:

glVertexAttribFormat(positionAttribute​, 3, GL_FLOAT, GL_FALSE​, 0);

由于格式与 VAO 相关联,因此每次创建 VAO 时,我都必须指定格式。 'position' 属性是 vec3 ,所以我将始终指定 3 和 GL_FLOATglVertexAttribFormat。 那么,为什么OpenGL以这种方式设计,以至于我每次创建VAO时都可能必须调用glVertexAttribFormat,并指定一种保持不变的格式呢? 在我看来,我应该在调用glGetAttribLocation时指定格式,这样我只做一次。

问问自己,当它们不匹配时会发生什么。

这允许指定与着色器用法本身不同的存储数据结构。假设你存储了打包的 rgba8,你仍然可能想用它做浮点数学。因此,您在着色器中将其声明为 vec4,但使用 RGBA8 作为顶点数据格式。GL将为您在2种格式之间进行转换。

嗯,设计决策是在每个 API 中做出的。完成给定任务的方法大多不只有一种,在某种程度上,这只是在这种情况下定义的方式。

我对这背后的决策没有任何直接的见解,但可以想到一些为这个提供有效动机的考虑因素。

  • 没有着色器的操作:OpenGL已经发展了很长时间。着色器最初不是 API 的一部分,并且仍有 API 版本可以在没有着色器的情况下运行(兼容性配置文件(。如果没有着色器,则从着色器属性派生属性类型的想法根本行不通。

  • 支持与 GLSL 类型不对应的数据类型:虽然在着色器中使用 vec4 之类的东西和属性规范的GL_FLOAT是最常见的,但这不是唯一的选择。例如,可以将属性指定为GL_HALF_FLOAT或疯狂的格式,如 GL_INT_2_10_10_10_REV 。如果您确实想为顶点数据节省内存,则这些格式可能具有用途。或者,如果你已有此格式的顶点,例如,因为你正在移植支持它们的 Direct3D 代码。这些格式不直接对应于 GLSL 中的类型,因此如果不显式指定属性类型,则无法支持它们。

  • 顶点设置状态和程序状态之间的依赖关系:这更具概念性。它现在的工作方式,顶点设置状态和程序状态是正交的。避免不必依赖的概念之间的依赖始终是一个好的设计目标。如果顶点属性格式依赖于当前绑定的程序,则会破坏这种独立性。例如,绑定新程序时,顶点属性数据的解释可能会更改,这意味着必须更新顶点设置状态。由于由此产生的复杂性和低效率,这些类型的状态依赖是非常不可取的。

那么,如果你有多个属性会发生什么?或者,如果要存储位置、颜色和法线怎么办?根据我的经验,这特别适用于紫外线坐标。

因为这样您的属性将只有大小为 2 而不是 3。因为你的纹理只有2个坐标(忽略3D纹理的可能性(。如果您在 2D 中工作怎么办。属性指针允许您指定该属性的输入大小,而不是其他任何内容以及类型。它很有用,因为它允许您更改数据的格式。在任何情况下,您都应该只创建一次 VAO,除非您需要将新数据重新绑定到缓冲区。

"在我看来,我应该指定格式

嗯,不,您没有指定任何格式,您只是获取了一个属性位置。

选择它只是因为GL在以下方面为您提供了很大的灵活性

  1. 将其他数据类型转换为浮点
  2. 传递具有不同大小的矢量
  3. 规范化非浮点型
  4. 对属性使用交错的非零偏移缓冲区

这只能"部分"自动化。当然,您可以自省着色器并找出您的属性确实是一个vec3,因此 3 个浮点数,但这仍然不能告诉 GL 顶点数组缓冲区对象中的数据是什么样子的。值得注意的是,它允许包含

  • 其他兼容类型(请注意,glVertexAttribPointer/glVertexAttribFormat可以采用一堆不同的类型,而不仅仅是GL_FLOAT(
  • 具有较低维度的数据(谁告诉 GL
  • 交错数据(谁告诉 GL 什么是进步?
  • 非零偏移量的数据(谁告诉 GL 什么是基本偏移量?
  • 可能需要也可能不需要规范化的数据(谁告诉 GL

回答"谁告诉":你对glVertexAttribFormat的呼唤。