【问题标题】:OpenGL How Many VAOsOpenGL 有多少 VAO
【发布时间】:2013-08-28 10:28:30
【问题描述】:

我正在编写一个 OpenGL3+ 应用程序,但对 VAO 的使用有些困惑。现在我只有一个 VAO,一个围绕原点的标准化四边形集。这个单一的 VAO 包含 3 个 VBO;一个用于位置,一个用于表面法线,一个 GL_ELEMENT_ARRAY_BUFFER 用于索引(所以我只能存储 4 个顶点,而不是 6 个)。

我已经设置了一些辅助方法来将对象绘制到场景中,例如drawCube(),它采用位置和旋转值并遵循程序;

  • 绑定quad VAO。
  • 每个立方体面:
    • 创建一个代表这张脸的模型矩阵。
    • 将模型矩阵上传到uniform mat4 model顶点着色器变量。
    • 调用 glDrawElements() 将四边形绘制到该面的位置。

我刚刚开始添加每个立方体颜色的任务,并意识到我不能将我的颜色 VBO 添加到单个 VAO,因为它会随着每个立方体而变化,这感觉不对。

我刚刚阅读了这个问题; OpenGL VAO best practices,它告诉我我的方法是错误的,我应该使用更多的 VAO 来节省每次设置整个场景的工作。

应该使用多少个 VAO? 显然我使用 1 个的方法不是最优的,场景中的每个静态表面是否应该有一个 VAO?那些会动的呢?

我正在为每个顶点写入一个统一变量,对吗? 我读到 uniform 着色器变量不应该在帧中更改,如果我能够将不同的值写入我的uniform 变量,制服与顶点着色器中的简单 in 变量有何不同?

【问题讨论】:

  • VAO 不包含任何 VBO。这是关于 VAO 如何工作以及它们的状态向量实际包含什么的常见误解。您可以完全初始化 VAO,而无需绑定 VBO。它不会让您将单个顶点推入管道,但这是可能的。话虽如此,在您的情况下,术语“VBO”是模棱两可的。通常,您将绑定到 GL_ARRAY_BUFFER 的缓冲区对象称为 VBO。这样的缓冲区对象与绑定到 GL_ELEMENT_ARRAY_BUFFER 的缓冲区对象具有不同的语义。后者的绑定实际上是 VAO 状态。但是,它们仍然只是缓冲区对象。
  • "我正在为每个顶点写入一个统一变量,对吗?" 这不仅不正确,而且不可能。要么你混淆了你的条款,要么你正在做一些你没有意识到你正在做的事情。
  • @NicolBolas 我将调用glDrawElements 与调用glUniformMatrix4 交错,通过更新模型矩阵(声明为uniform mat4 model)将相同的VAO 绘制到不同的位置。我只能假设这是可行的,因为我的立方体出现在预期的位置,并且没有对这些位置进行其他用途。
  • @thokra 谢谢,我觉得 ELEMENT 缓冲区不同,但没有意识到将 is 称为 VBO 是不正确的。我应该为我的世界中的每个多边形使用 VAO 吗?或者一个 VAO 用于所有具有相同纹理的表面,等等...我试图弄清楚应该使用什么粒度的 VAO。
  • @lynks:Nicol Bolas 的意思是,您无法更改单个绘图调用提交的每个顶点的统一变量的值。您只能在两次绘图调用之间更改制服,但不能为每个顶点更改制服,除非您为每个顶点发出绘图调用,这通常是愚蠢的,甚至不适用于除 POINTS 之外的任何基本类型。

标签: opengl vao


【解决方案1】:

显然我的 1 方法不是最优的,场景中的每个静态表面是否应该有一个 VAO?

绝对不是。切换 VAO 的成本很高。如果在场景中为每个对象分配一个 VAO,则需要在渲染此类对象之前切换 VAO。将其扩展到当前可见的数百或数千个对象,您将获得同样多的 VAO 更改。问题是,如果您有多个共享公共内存布局的对象,即元素的大小/类型/规范化/步幅相同,为什么要定义多个存储相同信息的 VAO?您可以使用相应的绘制调用直接控制要开始从中提取顶点属性的偏移量。

对于非索引几何,这是微不足道的,因为您为 gl[Multi]DrawArrays*() 提供了一个 first (或在多绘制情况下的偏移数组)参数,它定义了关联 ARRAY_BUFFER 的数据存储的偏移量。

对于索引几何体,如果您将多个对象的索引存储在单个 ELEMENT_ARRAY_BUFFER 中,您可以使用 gl[Multi]DrawElementsBaseVertex 为索引提供恒定偏移量,或者通过在将索引上传到缓冲区对象。

能够在缓冲存储中提供偏移量还意味着您可以将多个不同的对象存储在单个 ARRAY_BUFFER 中,并将相应的索引存储在单个 ELEMENT_ARRAY_BUFFER 中。但是,缓冲区对象的大小应取决于您的硬件,并且供应商的建议有所不同。

我正在为每个顶点写入一个统一变量,对吗?我读到统一着色器变量不应该在中间帧改变,如果我能够将不同的值写入我的统一变量,那么统一与顶点着色器中的简单变量有何不同?

首先,声明为 in/out 的制服和着色器输入/输出变量在各种情况下有所不同:

  • 输入/输出变量定义着色器阶段之间的接口,即一个着色器阶段中的输出变量由下一阶段中相应且同名的输入变量支持。如果使用相同的名称声明,统一在所有阶段都可用,并且在应用程序更改之前保持不变。

  • 顶点着色器中的输入变量由 ARRAY_BUFFER 填充。统一块内的统一支持 UNIFORM_BUFFER。

  • 也可以使用 glVertexAttrib*() 系列函数直接编写输入变量。单个制服是使用 glUniform*() 系列函数编写的。

  • 制服的值是程序状态。输入变量的值不是。

语义上的区别也应该很明显:制服,顾名思义,在一组图元中通常是恒定的,而输入变量通常每个顶点或片段都在变化(由于插值)。

编辑:澄清和考虑 Nicol Bolas 的评论:应用程序不能为单个绘制调用提交的一组顶点更改制服,也不能通过调用 glVertexAttrib* 来更改顶点属性()。由缓冲区对象支持的顶点着色器输入将在每个顶点或以 glVertexAttribDivisor 设置的某个特定速率更改一次。

EDIT2:为了阐明 VAO 在理论上如何存储多个布局,您可以简单地定义具有不同索引但语义相同的多个数组。例如,

glVertexAttribPointer(0, 4, ....);

glVertexAttribPointer(1, 3, ....);

可以定义两个数组,索引为 0 和 1,组件大小为 3 和 4,并且都引用顶点的位置属性。但是,根据您要渲染的内容,您可以绑定一个假设的顶点着色器输入

// if you have GL_ARB_explicit_attrib_location or GL3.3 available, use explicit
// locations
/*layout(location = 0)*/ in vec4 Position; 

/*layout(location = 1)*/ in vec3 Position;

明确索引 0 或 1 或 glBindAttribLocation() 并仍然使用相同的 VAO。 AFAIK,规范没有说明如果启用了属性但不是由当前着色器提供的属性会发生什么,但我怀疑在这种情况下实现只是忽略该属性。

您是否从相同或不同的缓冲区对象获取所述属性的数据是另一个问题,但当然可能。

我个人倾向于每个布局使用一个 VBO 和 VAO,即,如果我的数据由具有相同属性的相同数量的属性组成,我将它们放入单个 VBO 和单个 VAO。

一般来说:您可以很多试验这些东西。去做吧!

【讨论】:

  • 所以VAO对一个或另一个对象没有特别的依恋?这可能是我的困惑,因为在阅读我的代码时,我看到我通过调用glEnableVertexAttribArray()glBindBuffer() 将VBO 绑定到in 变量来将位置值写入VAO。这两个调用应该是渲染循环的一部分吗?为每个表面交换不同的“位置”VBO?现在它们是我设置的一部分。
  • VAO 存储顶点属性索引和相应缓冲区绑定之间的映射列表。当调用 glVertexAttribPointer 时,当前绑定到 ARRAY_BUFFER 的缓冲区对象的名称被记录为指定索引并存储在 VAO 中。这就是 VAO 关联索引和缓冲区对象的方式。对应的值可以通过 glGetVertexAttribiv() 和 VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 查询。另请参阅 GL 3.3 核心规范第 6.2 节(状态表)中的表 6.5。
  • 感谢您的解释。我想我一定对事情的运作方式有一个非常错误的想法。如果你有两个不同位置和纹理的四边形,你会用一个大的 VBO 来处理所有的事情而只有一个 VAO 吗?还是每个四边形有 4 个 VBO 用于位置和纹理?然后是一两个 VAO?我猜是一个 VAO,你在渲染周期中使用 glBindBuffer()glVertexAttribPointer() 重新绑定到位置和纹理 VBO?
  • 我建议使用单个 VBO/VAO。绝对没有理由需要将四边形放入单独的存储中,并且属性的内存布局显然是相同的,因此您不需要将内容放入单独的 VAO 中。另外,请参阅我的第二次编辑以了解更多信息。
  • @thokra 如果您对模型仅使用一个 VBO/VAO,如果对象数量发生变化,您如何保留缓冲区的大小?或者如果有一个新对象,您创建一个新的 VBO 并将其附加到现有的 VAO?
猜你喜欢
  • 1970-01-01
  • 2012-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-05
相关资源
最近更新 更多