使用多个 openGL VBO 绘制多个模型

Posted

技术标签:

【中文标题】使用多个 openGL VBO 绘制多个模型【英文标题】:Drawing multiple models using multiple openGL VBO's 【发布时间】:2014-02-02 17:31:14 【问题描述】:

我不会发布大量代码,而是直观地表达这个问题。我希望你明白我在说什么。

我正在制作一个游戏,在代码中我有一个模型类,它加载一个模型并为其设置 VBO。

在加载函数中,它生成一个新的 VBO ID,并通过绑定等方式将文件中的顶点数据加载到该缓冲区中。(这工作正常)

在程序开始时,我制作了一个模型对象并加载了一个 .obj 文件。

在渲染函数中,我只需调用 DrawArrays() 函数(带有必要的额外内容),正如我所说,一切正常,因为模型毫无问题地出现在屏幕上。

但问题是,当我在程序开始时创建两个模型对象并将不同的 .obj 文件加载到每个对象中时。

问题是当我玩的时候,屏幕上只绘制了第二个模型。

问题是因为我没有正确理解 VBO 的工作原理。

这就是我“认为”VBO 工作的方式。

您可以根据需要生成任意数量的 VBO ID。 您可以绑定每个 ID 以使其成为活动缓冲区。 您可以将顶点数据加载到该缓冲区中。 您现在有效地拥有许多不同的 ID,每个 ID 都引用“一组顶点数据”。 通过调用 DrawArray 函数,它会绘制您生成的每个缓冲区(有效地在屏幕上显示所有模型)

现在我知道这是错误的,因为在我有限的理解中,我看不出如何打开/关闭模型。但无论我看多少教程,我都无法回答这个问题,因为它们都专注于显示第一个 VBO,顺便说一句,我可以做到。

那么...如果有道理,谁能启发我?

【问题讨论】:

【参考方案1】:

你做的基本正确,但绘图命令绘制你生成的每个缓冲区。如果你想用不同缓冲区中的数据绘制 2 个对象,你必须发出 2 个绘图命令。

让我根据我的做法来解释一下:

您使用 glGenBuffers 创建缓冲区。在使用缓冲区之前,您必须“绑定”它,即使用 glBindBuffer 使其处于活动状态。当缓冲区处于活动状态时,您可以对其进行处理(例如填充数据)。请注意,有不同的绑定目标,例如,您可以拥有一个活动数组缓冲区(用于顶点数据)和一个活动元素数组缓冲区(用于索引数据)。

当你想画东西时,你必须明确你想画什么(我现在假设你正在使用你自己的着色器) 通常你至少指定你的顶点数据和索引数据。为此,首先将缓冲区与顶点数据绑定(作为数组缓冲区),然后使用属性 id 调用 glVertexAttribPointer。这告诉 OpenGL 绑定的缓冲区现在是当前属性的源。然后,将索引缓冲区绑定为元素数组缓冲区并调用 glDrawElements(不确定 glDrawArrays 在这里如何工作)。

您不能同时对同一属性使用多个 VBO - 绑定另一个缓冲区的调用只会覆盖前一个调用并使第二个缓冲区处于活动状态,我认为这就是为什么只有您的第二个对象被绘制的原因。

您还可以使用 VAO 来简化绘图 - 基本上您为绑定命令分配一个 id,然后您可以用一行执行它们(想想显示列表)。

所以,我的加载(一个对象)看起来像这样:

    创建缓冲区(假设缓冲区 1 用于顶点数据,缓冲区 2 用于索引数据)。 将缓冲区 1 绑定为数组缓冲区。 用顶点数据填充缓冲区。 对缓冲区 2 重复 2 和 3,分别作为元素数组缓冲区和索引数据。

还有我的画(同样是一个物体):

    将缓冲区 1 绑定为数组缓冲区。 使用 glVertexAttribPointer 指定此缓冲区转到特定属性。 将缓冲区 2 绑定为元素数组缓冲区。 调用 DrawElements

对于第二个对象,您必须重复所有内容。

【讨论】:

“你不能同时使用多个 VBO”——没错,但只能用于同一个属性,因为多个 VBO 可以同时用于一个单独的属性,当然(参见 ***.com/a/7223695/110118示例) @mlvljr:你当然是对的,我编辑了我的答案。我试图让答案变得非常基本 - 我相信如果你在开始处理法线、纹理坐标、VAO 等之前先了解基础知识会很有帮助。 旧答案,但您刚刚解决了我的问题。您能否详细说明每个对象都需要 glVertexAttribPointer 的“原因”?在我的情况下,每次的值都是相同的,但是,没有它,对象的位置不会更新。 glVertexAttribPointer 是否有重新读取数组的副作用?【参考方案2】:

按照以下步骤渲染模型,假设模型 1 的顶点存储在 vertexBuffer 中,模型 2 的顶点存储在 vertexBuffer2 中:

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);        
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, <no. of vert>);

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer2);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, <no. of vert>);

//swap buffers depending on your windowing toolkit

确保不要在两个模型渲染中间执行 glClear。 万事如意!

【讨论】:

可以确认这个基本方法有效;我在 Java 中使用 lwjgl,而不是调用 glVertexAttribPointer(...),而是调用(在我的特定情况下)glVertexPointer(3, GL_INT, vertexDataSize, 0) 我确认这种方法适用于原生 android 开发的 OpenGL ES。这是我的源代码:github.com/raydelto/opengles-md2loader-android【参考方案3】:

您要做的是在每次调用 drawArrays 之前将 vertexArrayAttribute 设置回第一个对象数据

draw arrays 函数使用存储在 VAO 中的绑定来查找渲染所有内容所需的数据

所以要渲染 2 个模型,您可以创建第二个 VAO 绑定它并根据需要调用 glVertexAttribPointer。对于绘图,您绑定 VAO 并为每个模型调用 drawArrays

【讨论】:

以上是关于使用多个 openGL VBO 绘制多个模型的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 上使用具有多个 VBO 和 IBO(多个对象)OpenGLES 2 的 VAO 进行绘制

多个网格、多个 VBO、多个 VAO、OpenGL 4.1

OpenGL 使用单个 VBO 渲染多个对象,并使用另一个 VBO 更新对象的矩阵

OpenGL:一个 VBO 的多个 VAO

OpenGL ES:一个 VBO 中的多个网格

OpenGL VBO 绘图问题