顶点缓冲对象(VBO)的用法

背景

这是我们第一次遇到GLEW,OpenGL扩展牧马人图书馆。GLEW帮助您处理可伴随OpenGL扩展管理的头痛。一旦初始化,它会查询您平台上的所有可用扩展,动态加载它们,并通过单个头文件轻松访问。

在本教程中,我们将首次看到顶点缓冲对象(VBO)的用法。顾名思义,它们用于存储顶点。您正在尝试可视化的3D世界中存在的对象,无论是怪物,城堡还是简单的旋转立方体,始终通过将一组顶点连接在一起构建。VBO是将顶点加载到GPU中的最有效的方式。它们是可以存储在视频存储器中的缓冲区,并为GPU提供最短的访问时间,因此它们是绝对推荐的。

本教程和下一个是本系列中唯一一个,我们将依靠固定功能管道而不是可编程的。实际上,在这两个教程中都没有发生变化。我们只是依靠数据流经管道的方式。管道的彻底研究将在下面的教程中介绍,但是现在已经足够明白,在到达光栅化器之前(实际上使用屏幕坐标绘制点,线和三角形),可见顶点的X,Y和Z坐标在范围[-1.0,1.0]。光栅化器将这些坐标映射到屏幕空间(例如,如果屏幕宽度为1024,则X坐标-1.0被映射到0,并且1.0映射到1023)。最后,光栅化器根据在绘图调用中指定的拓扑绘制原语(见下文的源语篇)。由于我们没有将任何着色器绑定到管道上,我们的顶点不会进行转换。这意味着我们只需要在上述范围内给它们一个值,以使它们可见。实际上,为X和Y两者选择零将顶点放在两个轴的精确中点 - 换句话说,就是屏幕的中间。

安装GLEW: GLEW可从其主网站 http://glew.sourceforge.net/获取大多数Linux发行版为它提供了预构建的包。在Ubuntu上,您可以通过从命令行运行以下命令来安装它:

apt-get install libglew1.6 libglew1.6-dev

来源walkthru

#include

这里我们包括单个GLEW标题。如果您包含其他OpenGL标题,您必须小心,以便其他人GLEW将抱怨该文件。为了将程序与GLEW链接,您需要在makefile中添加“-lGLEW”。

#include "math_3d.h"

在本教程中,我们开始使用像vector这样的帮助器结构。我们将继续扩大这个标题。

GLenum res = glewInit();
if (res != GLEW_OK)
{
    fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
    return 1;
}

这里我们初始化GLEW并检查是否有任何错误。这必须在GLUT初始化之后完成。

Vector3f Vertices[1];
Vertices[0] = Vector3f(0.0f, 0.0f, 0.0f);

我们创建一个Vector3f结构的数组(这个类型在math_3d.h中定义),并将XYZ初始化为零。这将使点出现在屏幕中间。

GLuint VBO;

我们在程序的全局部分中分配一个GLuint来存储顶点缓冲区对象的句柄。稍后会看到,大多数(如果不是全部)OpenGL对象可以通过一个GLuint类型的变量进行访问。

glGenBuffers(1, &VBO);

OpenGL定义了几个用于生成各种类型对象的glGen *函数。它们通常需要两个参数:第一个参数指定要创建的对象数,第二个参数是用于存储驱动程序为您分配的句柄的GLuint数组的地址(请确保数组足够大以处理请求!)。将来调用此函数将不会生成相同的对象句柄,除非您先用glDeleteBuffers删除它们。请注意,在这一点上,您不要指定您打算使用缓冲区,因此可以将其视为“通用”。这是下一个功能的工作。

glBindBuffer(GL_ARRAY_BUFFER, VBO);

OpenGL有一个非常独特的方式来使用句柄。在许多API中,句柄被简单地传递给任何相关的函数,并且该句柄被采取。在OpenGL中,我们将句柄绑定到目标名称,然后在该目标上执行命令。这些逗号会影响有界句柄,直到另一个绑定为止,或者上面的调用将作为句柄为零。目标GL_ARRAY_BUFFER意味着缓冲区将包含顶点数组。另一个有用的目标是GL_ELEMENT_ARRAY_BUFFER,这意味着缓冲区包含另一个缓冲区中顶点的索引。其他目标也可用,我们将在以后的教程中看到它们。

glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);

绑定我们的对象后,我们填写数据。上面的调用获取目标名称(与我们用于绑定的方式相同),数据的大小(以字节为单位),顶点数组的地址以及指示此数据的使用模式的标志。由于我们不会更改缓冲区内容,因此我们指定了GL_STATIC_DRAW。相反的是GL_DYNAMIC_DRAW。虽然这只是OpenGL的一个提示,对于使用正确的标志来说,是一件好事。驱动程序可以依靠它来进行优化启发式(如存储缓冲区的最佳位置)。

glEnableVertexAttribArray(0);

在着色器教程中,您将看到着色器(position,normal等)中使用的顶点属性具有映射到它们的索引,可以在C / C ++程序中的数据和着色器之间的属性名称之间创建绑定。另外您还必须启用每个顶点属性索引。在本教程中,我们还没有使用任何着色器,但是我们加载到缓冲区中的顶点位置在固定函数管道中被视为顶点属性索引0(当没有着色器绑定时,它将变为活动状态)。您必须启用每个顶点属性,否则数据将无法通过管道访问。

glBindBuffer(GL_ARRAY_BUFFER, VBO);

在我们准备进行绘图调用时,我们再次绑定缓冲区。在这个小程序中,我们只有一个顶点缓冲区,所以每个帧都调用这个调用是冗余的,但是在更复杂的程序中有多个缓冲区可以存储各种模型,你必须用要使用的缓冲区来更新流水线状态。

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

该通知告诉管道如何解释缓冲区内的数据。第一个参数指定属性的索引。在我们的例子中,我们知道它默认为零,但是当我们开始使用着色器时,我们将需要在着色器中显式设置索引或查询它。第二个参数是属性中的组件数(X,Y和Z为3)。第三个参数是每个组件的数据类型。下一个参数指示我们是否希望我们的属性在管道中被使用之前被归一化。我们的案例我们希望数据通过不变。第五个参数(称为“stride”)是缓冲区中该属性的两个实例之间的字节数。当只有一个属性(例如 缓冲区只包含顶点位置),数据紧密包装,我们传递值为零。如果我们有一个包含位置和法线的结构数组(每个都是3个浮点的向量),我们将以字节(6 * 4 = 24)传递结构的大小。在上一个例子的情况下,最后一个参数很有用。我们需要在管道将找到我们的属性的结构中指定偏移量。在具有位置和正常结构的情况下,位置的偏移为零,而正常的偏移为12。我们需要在管道将找到我们的属性的结构中指定偏移量。在具有位置和正常结构的情况下,位置的偏移为零,而正常的偏移为12。我们需要在管道将找到我们的属性的结构中指定偏移量。在具有位置和正常结构的情况下,位置的偏移为零,而正常的偏移为12。

glDrawArrays(GL_POINTS, 0, 1);

最后,我们调用绘制几何。我们迄今为止看到的所有命令都很重要,但它们只为draw命令设置了舞台。这是GPU真正开始工作的地方。它现在将绘制调用的参数与构建到此点的状态相结合,并将结果呈现给屏幕。

OpenGL提供了几种类型的绘图调用,每种类型都适用于不同的情况。一般来说,您可以将它们划分为两类:有序绘制和索引绘制。有序绘制更简单。GPU遍历顶点缓冲区,逐个遍历顶点,并根据绘图调用中指定的拓扑进行解释。例如,如果您指定GL_TRIANGLES,则顶点0-2将成为第一个三角形,3-5个第二个等等。如果希望相同的顶点出现在多个三角形中,则需要在顶点缓冲区中指定两次,这是浪费空间。

索引绘制更复杂,并涉及一个称为索引缓冲区的附加缓冲区。索引缓冲区包含顶点缓冲区中顶点的索引。GPU扫描索引缓冲区,并以与上述描述类似的方式,索引0-2成为第一个三角形,依此类推。如果您想要在两个三角形中的相同顶点在索引缓冲区中简单地指定其索引两次。顶点缓冲区只需要包含一个副本。索引绘制在游戏中更常见,因为大多数模型都是由三角形创建的,这些三角形代表一些表面(人的皮肤,城堡墙壁等),它们之间有许多顶点共享。

在本教程中,我们使用最简单的绘图调用 - glDrawArrays。这是一个有序的绘图,所以没有索引缓冲区。我们将拓扑指定为点,这意味着每个顶点都是一个点。下一个参数是要绘制的第一个顶点的索引。在我们的例子中,我们要在缓冲区的开始处开始,所以我们指定零,但是这样我们可以在同一个缓冲区中存储多个模型,然后根据缓冲区中的偏移量选择一个模型。最后一个参数是要绘制的顶点数。

glDisableVertexAttribArray(0);

在不立即使用时,禁用每个顶点属性是很好的做法。当着色器不使用时,使其启用是确定的问题的方式。

你可能感兴趣的:(openGL)