在OpenGL中绑定点的目的?

时间:2016-10-07 16:23:36

标签: c++ opengl opengl-es vertex-buffer vertex-array-object

我不明白OpenGL中绑定点(例如GL_ARRAY_BUFFER)的目的是什么。据我所知glGenBuffers()创建了一种指向位于GPU内存中某处的顶点缓冲区对象的指针。

所以:

glGenBuffers(1, &bufferID)

意味着我现在有一个句柄,bufferID,到图形卡上的1个顶点对象。现在我知道下一步是将bufferID绑定到绑定点

glBindBuffer(GL_ARRAY_BUFFER, bufferID)

这样我就可以使用该绑定点使用glBufferData()函数向下发送数据,如下所示:

glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW)

但为什么我不能只使用bufferID来指定我想要发送数据的位置呢?类似的东西:

glBufferData(bufferID, sizeof(data), data, GL_STATIC_DRAW)

然后,当调用绘图函数时,我也只是将任何ID放入我希望绘制函数绘制的VBO中。类似的东西:

glDrawArrays(bufferID, GL_TRIANGLES, 0, 3)

为什么我们需要使用glBindBuffers进行间接的额外步骤?

1 个答案:

答案 0 :(得分:10)

OpenGL使用对象绑定点做两件事:指定一个对象作为渲染过程的一部分,并能够修改对象。

为什么它将它们用于前者很简单:OpenGL需要很多对象才能呈现。

考虑一下你过于简单化的例子:

glDrawArrays(bufferID, GL_TRIANGLES, 0, 3)

该API不允许我将单独的顶点属性来自不同的缓冲区。当然,您可以提出glDrawArrays(GLint count, GLuint *object_array, ...)。但是,如何将特定缓冲区对象连接到特定的顶点属性?或者,如何从缓冲区0中获取2个属性,从缓冲区1中获取第三个属性?这些是我现在可以用当前的API做的事情。但你提议的人无法处理它。

即便如此,也需要将需要渲染的许多其他对象放在一边:程序/管道对象,纹理对象,UBO,SSBO,转换反馈对象,查询对象等。拥有所有在单个命令中指定的所需对象从根本上是不可行的(并且不考虑性能成本)。

每次API需要添加新类型的对象时,您都必须添加glDraw*函数的新变体。现在,有over a dozen such functions。你的方式会给我们数百

相反,OpenGL定义了一种方式让你说出#34;下次渲染时,以这种方式使用这个对象进行该过程。"这就是使用对象的绑定意味着什么。

  

但为什么我不能仅使用bufferID来指定我想要发送数据的位置呢?

这是为了修改对象而绑定一个对象,而不是说它将被使用。那是......另一回事。

显而易见的答案是,"你不能这样做,因为OpenGL API(直到4.5)没有让你这样做的功能。"但我更怀疑这个问题是为什么OpenGL没有这样的API(直到4.5,其中glNamedBufferStorage存在)。

事实上,4.5确实具有这样的功能的事实证明了4.5之前的OpenGL的绑定对象修改API没有技术原因。这真的是一个"决定"由于遵循阻力最小的路径,OpenGL API从1.0发展而来。反复。

事实上,OpenGL所做的每一个错误决定都可以追溯到API中阻力最小的路径。但我离题了。

在OpenGL 1.0中,只有一种对象:显示列表对象。这意味着甚至纹理没有存储在对象中。因此,每次切换纹理时,都必须使用glTexImage*D重新指定整个纹理。这意味着重新上传它。现在,您可以(并且人们确实)将每个纹理的创建包装在显示列表中,这允许您通过执行该显示列表来切换纹理。希望驱动程序会意识到你正在这样做,而是适当地分配视频内存等等。

因此,当1.1出现时,OpenGL ARB意识到这是多么令人难以置信的愚蠢。因此,他们创建了纹理对象,它封装了纹理的内存存储和内部的各种状态。当你想使用纹理时,你绑定它。但有一个障碍。即,如何更改

请参阅,1.0有一堆已经存在的函数,如glTexImage*DglTexParamter等。这些修改纹理的状态。现在,ARB可以添加新功能,这些功能可以做同样的事情但是将纹理对象作为参数。

但这意味着将所有OpenGL用户划分为2个阵营:使用纹理对象的人和不使用纹理对象的人。这意味着,如果你想使用纹理对象,你必须重写修改纹理的现有代码的所有。如果你有一些函数在当前纹理上进行了大量的glTexParameter调用,则必须更改该函数以调用新的纹理对象函数。但是你必须改变调用它的你的函数,以便它作为参数获取它所操作的纹理对象。

如果该功能不属于您(因为它是您正在使用的库的一部分),那么您甚至无法

因此,ARB决定保留这些旧功能,并根据纹理是否绑定到上下文,让它们表现不同。如果绑定了一个,那么glTexParameter / etc会修改绑定纹理,而不是上下文的正常纹理。

此决定 已成立the general paradigm shared by almost all OpenGL objects

ARB_vertex_buffer_object出于同样的原因使用了这个范例。请注意各种gl*Pointer函数(glVertexAttribPointer等)如何与缓冲区相关联。您必须将缓冲区绑定到GL_ARRAY_BUFFER,然后调用其中一个函数来设置属性数组。当缓冲区绑定到该槽时,该函数将选择该缓冲区并将指针视为在调用*Pointer函数时绑定的缓冲区的偏移量。

为什么呢?出于同样的原因:易于兼容(或促进懒惰,取决于您希望如何看待它)。 ATI_vertex_array_object必须为gl*Pointer函数创建新的模拟。而ARB_vertex_buffer_object只是背负了现有的入口点。

用户无需从使用glVertexPointer更改为glVertexBufferOffset或其他一些功能。他们所要做的就是在调用设置顶点信息的函数之前绑定一个缓冲区(当然还要将指针更改为字节偏移)。

这也意味着他们没有必要添加一堆glDrawElementsWithBuffer - 类型的函数来渲染来自缓冲区对象的索引。

所以这在短期内并不是一个坏主意。但与大多数短期决策一样,随着时间的推移,它开始变得不那么合理了。

当然,如果您有权访问GL 4.5 / ARB_direct_state_access,您可以采用他们原本应该完成的方式。