使用 glDrawArrays 绘制多个模型

Posted

技术标签:

【中文标题】使用 glDrawArrays 绘制多个模型【英文标题】:Drawing Multiple Models with glDrawArrays 【发布时间】:2013-09-06 16:51:48 【问题描述】:

我有一个结构来存储 2 个不同模型的顶点数据,一个是立方体,另一个是金字塔。

struct Model
    GLuint vboID;
    GLfloat* vbo;
    GLuint vaoID;
    GLfloat* vao; 
    GLuint vertexStart;
    GLuint vertexCount;
;

我创建 vbo 并像这样生成它们的缓冲区:

Model cubeModel;
Model pyramidModel;

cubeModel.vbo = cubeVerts; //A GLfloat array I created earlier in code earlier. 
cubeModel.vertexCount= sizeof(cubeVerts);//size of the GLfloat array in bytes

pyramidModel.vbo = pyVerts; 
pyramidModel.vertexCount= sizeof(pyVerts);

glGenBuffers(1, &cubeModel.vboID); //Generate a buffer for the vertices
glBindBuffer(GL_ARRAY_BUFFER, cubeModel.vboID); //Bind the vertex buffer
glBufferData(GL_ARRAY_BUFFER, cubeModel.vertexCount, cubeModel.vbo, GL_STATIC_DRAW);

然后我使用:

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, cubeModel.vertexCount);
glDisableClientState(GL_VERTEX_ARRAY);

哪个有效,我成功绘制了立方体,但我也一直在尝试多种方法来绘制金字塔。

我需要做什么才能同时在屏幕上渲染?

编辑:这是我专门尝试过的,复制 glBufferData() 调用并传入金字塔数据,然后制作 glDrawArays(GL_TRIANGLES,0,cubeModel.drawcount+pyramidModel.drawCount) 计算顶点数据会堆叠,而 glDrawArrays 会一口气遍历所有传递的几何图形。

我也尝试过从 glGenBuffers() 到 glDisableClientState() 的 2 组指令,而是使用金字塔模型的所有数据。这很有趣,因为我的 glDrawArrays 是:

glDrawArrays(GL_TRIANGLES, 0, cubeModel.drawCount);
glTranslatef(4.0f,0.0f,0.0f);
glDrawArrays(GL_TRIANGLES, 0, pyramidModel.drawCount);

它最终绘制了一个非常混乱的金字塔 2 次,让我相信当我第二次调用 glBufferData() 两次时会覆盖之前传递的数据。

编辑 2:阅读 Andon 的评论后,我编辑了一些代码以澄清事情,drawCount 现在是 vertexCount,m_vertexBuffer 现在正确引用了我存储在 cubeModel 中的句柄,cubeModel.vboID,而不是使用旧的类变量 I是用来存放手柄的。

【问题讨论】:

你对 VBO 的理解是错误的......当我看到一个类/结构的字段名为 vbo 时,我首先想到的是:“这必须是一个 OpenGL 名称(句柄)到 VBO”。另一方面,您正在使用此字段来存储您将填充 VBO 的实际数据。与其按照现在的方式进行操作,不如尝试将 vbo 设置为 GLuint 并执行以下操作:glGenBuffers (1, &cubeModel.vbo)。您可以将顶点数据存储在任何您想要的位置,因为在将其提供给 VBO 之后,您就不再需要它了。为了清楚起见,也使用vertexCount 而不是drawCount 我知道只要有人说什么,我就会有一个捂脸的时刻。我也确实将其存储在结构中。模型结构中的 GLuint vboID。所以在这种情况下 cubeModel.vboID 【参考方案1】:

我不想混淆 cmets 部分,因为您的代码有很多问题。但我现在也没有时间把它们都看完,所以这是一种临时的答案。

glBufferData (...) 在技术上并没有覆盖以前传递的数据,它做的事情要糟糕得多。每次您调用glBufferData (...) 时,它都会创建一个您传递的大小的新数据存储,并(可选)用您提供的数据填充它(如果您传递非NULL 的内容)。把它想象成调用free (...) 然后malloc (...)delete [] ...new Type [...]

glBufferSubData (...) 是将顶点附加到顶点缓冲区的首选技术。不幸的是,它不会调整数据存储的大小。有时,过度分配您的 VBO 并将数据推迟到稍后提供(使用 glBufferSubData)会很方便。

如果您使用一个 VBO 来存储两个模型,您需要知道每个模型的起始顶点和顶点数,因为它与您的 VBO 数据相关。现在你只知道顶点的数量(你把它命名为drawCount而不是vertexCount)。对于大多数人来说,drawCount 表示您想要绘制某物的次数,而不是它包含的顶点或元素的数量。

调用glBufferData (...) 的大小字段应该是顶点数据的大小(以字节为单位)。现在你正在传递顶点的数量。您很可能打算使用sizeof (YourVertexDataStructure) 来表示单个顶点的大小(在这种情况下为sizeof (float [3])),以及类似sizeof (cubeVerts) / sizeof (YourVertexDataStructure) 的东西来计算实际存储在此数组中的顶点数。那么您传递给glBufferData (...) 的大小将是:_size * count_

【讨论】:

为了澄清起见,我对原始帖子进行了一些更改,将 drawCount 更改为 vertexCount,我还将模型结构的其余部分放在那里并引用了位于结构中的句柄。很高兴了解 glBufferData(...) 我对 OpenGL 非常陌生,所以我为我糟糕的代码编写道歉。 @JayPC:没问题,我通常讨厌给人们诸如“去阅读这样的教程”之类的答案,但在这种情况下,我认为最好看看涵盖 VBO 的 OpenGL 教程创建。我建议:arcsynthesis.org/gltut/Basics/…,它是由这里的一位常客编写的,并且是关于 Modern OpenGL 的更大系列教程的一部分,如果你从它开始而不是随机的点点滴滴在 Internet 上,您可以在很短的时间内启动并使用着色器。 我知道这种感觉,我实际上已经读过那个,我正在将直接模式项目转换为现代代码。另外 sizeof(cubeVerts) = 432 所以它已经以字节为单位(模型有 108 个大小为 GLfloat 的元素)所以 cubeModel.vertexCount 已经是字节数。我使用: sizeof(cubeVerts)/sizeof(GLfloat) 来查找有多少元素。我知道参数是以字节为单位的,因为我在这里查找了方法->link,实际上我以前使用过着色器,但在 LWJGL/Java 和直接模式下 @JayPC:好的,但是当您调用glDrawArrays (...) 时,您使用的是字节大小,它需要顶点数而不是总字节数。这就是为什么区分两者是有用的。存储顶点数并在需要时做一些额外的工作来计算以字节为单位的大小通常更有用。您当前的代码肯定会起作用(从某种意义上说,它将至少绘制所有顶点),因为以字节为单位的大小总是大于顶点的数量,但是对于超出数组末尾的每个顶点,您正在调用 undefined行为。 @JayPC:不,您不必合并数组。您可以为每个模型使用 VBO。然后您将更改绑定的 VBO 和顶点指针并绘制。 VAO 使这变得容易得多,您可以设置一个 VAO,为您的模型绑定 VBO,并正确设置所有顶点指针,然后您只需在想要绘制不同模型时更改绑定的 VAO。跨度>

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

为啥我的 glDrawArrays 没有绘制我存储的点?

glDrawArrays 仅在原点绘制一个点(在 ubuntu 上带有 glut 的 OpenGL)

不同形状的OpenGL glDrawArrays

我可以使用顶点着色器来显示模型法线吗?

用于三角形的 OpenGL 3.3 glDrawArrays

尝试从帧缓冲区传递纹理会导致 L_INVALID_OPERATION:glDrawArrays:绘制的源纹理和目标纹理相同