OpenGL 将对象绑定点用于两件事:指定要用作渲染过程的一部分的对象,以及能够修改该对象。
为什么将它们用于前者很简单:OpenGL 需要大量对象才能进行渲染。
考虑您过于简单的示例:
glDrawArrays(bufferID, GL_TRIANGLES, 0, 3)
该 API 不允许我将单独的顶点属性来自单独的缓冲区。当然,你可能会提议glDrawArrays(GLint count, GLuint *object_array, ...)
。但是如何将特定的缓冲区对象连接到特定的顶点属性呢?或者你怎么有来自缓冲区 0 的 2 个属性和来自缓冲区 1 的第三个属性?这些是我现在可以使用当前 API 做的事情。但你提议的一个不能处理它。
即便如此,您需要渲染的许多其他对象都放在一边:程序/管道对象、纹理对象、UBO、SSBO、变换反馈对象、查询对象等。在单个命令中指定所有需要的对象基本上是行不通(并且撇开性能成本不谈)。
每次 API 需要添加一种新类型的对象时,您都必须添加新的glDraw*
函数变体。而现在,这样的功能有十几个。你的方式会给我们数百个。
所以相反,OpenGL 定义了一些方式让你说“下次我渲染时,以这种方式使用这个对象来完成那个过程”。这就是绑定对象以供使用的含义。
但是为什么我不能只使用 bufferID 来指定我想将数据发送到哪里呢?
这是为了修改对象而绑定对象,而不是说它会被使用。那是……另一回事。
显而易见的答案是,“你不能这样做,因为 OpenGL API(直到 4.5)没有让你这样做的功能。” 但我宁愿怀疑这个问题真的是为什么 OpenGL 没有这样的 API(直到 4.5,那里glNamedBufferStorage
和这样的存在)。
事实上,4.5 确实有这些功能的事实证明,4.5 之前的 OpenGL 的 bind-object-to-modify API 没有技术原因。这确实是 OpenGL API 从 1.0 演变而来的“决定”,这要归功于遵循阻力最小的路径。反复。
事实上,OpenGL 所做的几乎每一个错误决定都可以追溯到采用 API 中阻力最小的路径。但我离题了。
在 OpenGL 1.0 中,只有一种对象:显示列表对象。这意味着即使纹理也没有存储在对象中。因此,每次切换纹理时,都必须使用 重新指定整个纹理glTexImage*D
。这意味着重新上传它。现在,您可以(并且人们确实)将每个纹理的创建包装在一个显示列表中,这允许您通过执行该显示列表来切换纹理。希望驱动程序会意识到您正在这样做,而是适当地分配视频内存等等。
所以当 1.1 出现时,OpenGL ARB 意识到这是多么愚蠢。所以他们创建了纹理对象,它封装了纹理的内存存储和其中的各种状态。当你想使用纹理时,你绑定它。但是有一个障碍。即,如何改变它。
看,1.0 有一堆已经存在的函数,比如glTexImage*D
,glTexParamter
等等。这些修改纹理的状态。现在,ARB 可以添加新的函数来做同样的事情,但将纹理对象作为参数。
但这意味着将所有 OpenGL 用户分成两个阵营:使用纹理对象的和不使用纹理对象的。这意味着,如果您想使用纹理对象,您必须重写所有修改纹理的现有代码。如果您有一些函数对当前纹理进行了一系列glTexParameter
调用,则必须更改该函数以调用新的纹理对象函数。但是您还必须更改调用它的函数,以便它将操作的纹理对象作为参数。
如果那个函数不属于你(因为它是你正在使用的库的一部分),那么你甚至不能这样做。
因此,ARB 决定保留这些旧函数,并根据纹理是否绑定到上下文来简单地让它们表现不同。如果已绑定,则glTexParameter
/etc 将修改绑定的纹理,而不是上下文的正常纹理。
这一决定建立了几乎所有 OpenGL 对象共享的通用范式。
ARB_vertex_buffer_object 出于同样的原因使用此范例。注意各种gl*Pointer
函数(glVertexAttribPointer
等等)是如何与缓冲区相关的。您必须将缓冲区绑定到GL_ARRAY_BUFFER
,然后调用其中一个函数来设置属性数组。当缓冲区绑定到该插槽时,该函数将拾取它并将指针视为在*Pointer
调用函数时绑定的缓冲区的偏移量。
为什么?出于同样的原因:易于兼容(或促进懒惰,取决于您希望如何看待它)。ATI_vertex_array_object 必须为这些gl*Pointer
函数创建新的类似物。而 ARB_vertex_buffer_object 只是捎带了现有的入口点。
用户不必更改使用glVertexPointer
toglVertexBufferOffset
或其他一些功能。他们所要做的就是在调用设置顶点信息的函数之前绑定一个缓冲区(当然还要更改指向字节偏移的指针)。
这也意味着他们不必添加一堆glDrawElementsWithBuffer
-type 函数来使用来自缓冲区对象的索引进行渲染。
所以这在短期内不是一个坏主意。但与大多数短期决策一样,随着时间的推移,它开始变得不那么合理。
当然,如果您可以访问 GL 4.5/ARB_direct_state_access,您就可以按照他们原本应该做的方式做事。