我认为你混淆了很多不同的事情并且有一些困惑,所以我尝试按照你提出的顺序解决其中的大多数问题:
当我们声明一系列顶点时,比如说形成一个三角形基元的 3 个顶点,我们基本上不会将它们存储在任何地方,它们只是在代码中声明。
不。如果您“无处”存储数据,那么您就没有它。你也混淆了宣言, 定义 and 初始化这里的变量。对于顶点数据(与所有其他形式的数据一样),有两种基本策略:
-
您将数据存储在某个地方,通常是在文件中。直接在源代码中指定它仅意味着它存储在某个二进制文件中,可能是可执行文件本身(或其使用的某些共享库)
-
You 程序上通过一些数学公式或更一般地通过一些数学公式生成数据算法
方法1和方法2当然可以混合使用,通常方法2需要一些参数(它本身需要存储在某个地方,所以参数又是情况 1)。
并且,通过相同的 VBO,我们将所有顶点信息发送到顶点着色器(这是一堆代码)。现在,VBO 位于 GPU 中,因此当我们调用 VBO 时,我们基本上将所有信息存储在 GPU 内存中。
OpenGL 实际上只是一个规范,它完全不知道 GPU 的存在和 VRAM 的存在。因此,OpenGL 使用concept of 缓冲对象(BO)作为一定大小的连续内存块完全由 GL 实施管理。作为用户,您可以要求 GL 创建或销毁此类 BO,指定它们的大小,并完全控制内容 - 如果您愿意,您可以将 MP3 文件放入 BO(并不是说有一个很好的用例)这)。
另一方面,GL 实现控制该内存的实际分配位置,以及 GPU 的 GL 实现
实际上具有专用视频内存的设备可以选择将 BO 直接存储在 VRAM 中。这hints like GL_STATIC_DRAW
在那里帮助 GL 实现决定在哪里最好地放置这样的缓冲区(但是提示系统有些缺陷,并且现代 GL 中存在更好的替代方案,但我不会在这里讨论)。GL_STATIC_DRAW
表示您打算指定内容一次并使用可能的次数作为source绘图选项的 - 因此数据不会经常更改(当然不会以每帧为基础或更频繁地更改),如果存在这种情况,将其存储在 VRAM 中可能是一个非常好的主意。
然后,作为管道渲染过程一部分的顶点着色器“来到”GPU 内存,“查看”VBO 并检索所有信息。
我认为可以这样说,尽管有些 GPU 有一个专用的“顶点获取”硬件阶段,它实际上读取顶点数据,然后将其馈送到顶点着色器。但这并不是真正重要的一点 - 顶点着色器需要访问每个顶点的数据,这意味着 GPU 将在顶点着色器执行之前或期间的某个时刻读取该内存(VRAM 或系统内存或其他)。
换句话说,VBO存储的是顶点数据(三角形顶点)
是的。用作顶点着色器的每顶点输入(“顶点属性”)源的缓冲区对象称为顶点缓冲对象(“VBO”),因此直接遵循该术语的定义。
并将其发送到顶点着色器。
我不会这么说。 BO只是一块内存,它并不主动do任何事物。它只是一个被动元素:它正在被写入或被读取。就这样。
// here I declare the VBO
unsigned int VBO;
不,您正在编程语言的上下文中声明(并定义)一个变量,并且该变量稍后用于保存name缓冲区对象的。在 GL 中,对象names只是正整数(因此 0 被保留给 GL 作为“没有这样的对象”或“默认对象”,具体取决于对象类型)。
// we have 1 VBO, so we generate an ID for it, and that ID is: GL_ARRAY_BUFFER
glGenBuffers(1, &VBO)
No. glGenBuffers(n,ptr)
只是生成names for n
新的缓冲区对象,所以它会生成n
以前未使用的缓冲区名称(并将它们标记为已使用),并通过将它们写入指向的数组来返回它们ptr
。因此,在这种情况下,它只是创建一个新的缓冲区对象名称并将其存储在您的VBO
多变的。
GL_ARRAY_BUFFER
与此无关。
// GL_ARRAY_BUFFER is the VBO's ID, which we are going to bind to the VBO itself
glBindBuffer(GL_ARRAY_BUFFER, VBO)
No, GL_ARRAY_BUFFER
不是VBO的ID,而是你的值VBO
变量是 VBO 的 ID(名称!)。GL_ARRAY_BUFFER
is the 结合目标。 OpenGL 缓冲区对象可用于不同的目的,将它们用作顶点数据的源只是其中之一,并且GL_ARRAY_BUFFER
指的是该用例。
请注意,经典 OpenGL 使用的概念binding有两个目的:
-
绑定使用:每当您发出依赖于某些 GL 对象的 GL 调用时,您想要使用的对象当前必须绑定到某些(特定的,取决于用例)绑定目标(不仅是缓冲区对象,还包括纹理和其他对象) )。
-
绑定到修改:每当您作为用户想要修改某个对象的状态时,您必须先将其绑定到某个绑定目标,并且所有对象状态修改函数都不直接将要处理的GL对象的名称作为参数,但绑定目标,并且会影响当前绑定在该目标的对象。 (现代GL也有直接状态访问它允许您修改对象而不必先绑定它们,但我也不会在这里详细介绍这一点)。
将缓冲区对象绑定到某些缓冲区对象绑定目标意味着您可以将该对象用于目标定义的目的。但请注意,缓冲区对象不会更改,因为它绑定到目标。您可以将缓冲区对象绑定到不同的目标即使在同一时间。 GL 缓冲区对象没有类型。将缓冲区称为“VBO”通常仅意味着您打算将其用作GL_ARRAY_BUFFER
,但GL不在乎。它确实关心缓冲区的绑定是什么GL_ARRAY_BUFFER
在当时的glVertexAttribPointer()
call.
// bunch of info in here that basically says: I want to take the vertex data (the
// triangle that I declared as a float) and send it to the VBO's ID, which is
// GL_ARRAY_BUFFER, then I want to specify the size of the vertex
// data, the vertex data itself and the 'static draw' thingy
glBufferData(...).
Well, glBufferData
只是创建实际的数据存储对于GL缓冲区对象(即真实内存),这意味着您指定缓冲区的大小(以及我之前提到的使用提示,您告诉GL您打算如何使用内存),并且它可选地允许您初始化通过将数据从应用程序内存复制到缓冲区对象来访问缓冲区。它不关心实际数据和您使用的类型)。
自从你使用GL_ARRAY_BUFFER
这里作为目标参数,该操作会影响当前绑定的BOGL_ARRAY_BUFFER
.
完成所有这些操作后,VBO 现在包含其中的所有顶点数据。
基本上,是的。
所以我们告诉 VBO,现在将其发送到顶点着色器。
否。GL 使用顶点数组对象(VAO),它存储每个顶点着色器输入属性在哪里查找数据(在哪个缓冲区对象中,在缓冲区对象内的哪个偏移处)以及如何解释该数据(通过指定数据类型)。
稍后在绘制调用期间,GL 将从缓冲区对象内的相关位置获取数据,如您在 VAO 中指定的那样。如果此内存访问是由顶点着色器本身触发的,或者如果有一个专用的顶点获取阶段读取之前的数据并将其转发到顶点着色器 -或者如果有 GPU- 完全是特定于实现的,与您无关。
这就是管道的开始,仅仅是开始。
好吧,取决于你如何看待事物。在传统的基于光栅化器的渲染管线中,“顶点获取”或多或少是第一阶段,顶点缓冲区对象将仅保存从中获取顶点数据的内存(以及告诉它要使用哪些缓冲区对象,以及哪些实际位置以及如何解释它们)。