OpenGL中绑定点的目的?

Posted

技术标签:

【中文标题】OpenGL中绑定点的目的?【英文标题】:Purpose of binding points in OpenGL? 【发布时间】:2016-10-07 16:23:35 【问题描述】:

我不明白 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 进行额外的间接步骤?

【问题讨论】:

"意味着我现在有一个句柄,bufferID,到 1 个顶点对象"到一个 缓冲区对象。没有“顶点对象”这样的东西。 "为什么我不能只使用 bufferID 来指定我想将数据发送到哪里?"...ARB_direct_state_access? 【参考方案1】:

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 为您定义了“下次渲染时,以这种方式在该过程中使用此对象”的方式。这就是绑定对象以供使用的意义。


但是为什么我不能只使用 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*DglTexParamter 等等。这些修改纹理的状态。现在,ARB 可以添加新的函数来做同样的事情,但将纹理对象作为参数。

但这意味着将所有 OpenGL 用户分为两个阵营:使用纹理对象的阵营和不使用纹理对象的阵营。这意味着,如果您想使用纹理对象,则必须重写 所有 修改纹理的现有代码。如果您有某个函数对当前纹理进行了一堆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,您就可以按照他们原本应该做的方式做事。

【讨论】:

感谢您对这个基于历史过程的解释。当你开始使用 OpenGL 时,你必须消化的所有过于神秘、冗长、复杂的废话最终在解释了一些设计合理性时才开始变得有意义。它并没有使代码看起来更漂亮,但它确实有助于我理解如何导航 API。

以上是关于OpenGL中绑定点的目的?的主要内容,如果未能解决你的问题,请参考以下文章

关于opengl的绑定和解绑的时机

从帧缓冲区对象读取时,OpenGL 纹理坐标是相反的

用OpenGL进行立方体表面纹理贴图

用于在大满贯中绘制特征点的opengl着色器[关闭]

OpenGL——点的绘制(使用OpenGL来绘制可旋转坐标系的螺旋线)

在 OpenGL 上绘制点的意外 gluLookAt 行为