理解glVertexAttribPointer、glEnableVertexAttribArray、VAO、VBO的关系

本文详细介绍了OpenGL中VAO和VBO的概念及其工作原理。VAO作为一个容器,用于存储顶点属性设置;VBO则作为图形卡上的内存缓冲区,用于存储顶点数据。二者结合使用能显著提高渲染效率。

OpenGL抛弃glEnable(),glColor(),glVertex(),glEnable()这一套流程的函数和管线以后,就需要一种新的方法来传递数据到Graphics Card来渲染几何体,我们可以用VBO, 在3+版本我们可以使用Vertex Array Object-VAO,VAO是一个对象,其中包含一个或者更多的Vertex Buffer Objects。而VBO是Graphics Card中的一个内存缓冲区,用来保存顶点信息,颜色信息,法线信息,纹理坐标信息和索引信息等等。

       VAO在Graphics Card线性的存储几个对象信息,替代了以前发送我们需要的数据到Graphics Card上,这也是Direct3D没有立即模式情况下工作的方法,这就意味着应用程序不需要传输数据到Graphics Card上而得到较高的性能。一个VAO有多个VBO,如下图所示:

VAO是一个容器,它将所有可以由glVertexAttribPointer和其它一些函数进行设置的状态包装到一起。在使用一个VAO时,所有通过一次glVertexAttribPointer调用来指定的状态都会被存储到当前的VAO中,glVertexAttribPointer 指定了渲染时索引值为 index 的顶点属性数组的数据格式和位置。它们之间也是通过上下文,只有唯一的激活VAO,在VAO后创建的VBO都属于该VAO。关联VBO数据用取得当前激活的缓存区对象偏移来指定。

说白了,VAO可以想象为是一个大瓶子;VBO是一个内存缓冲区,用来保存顶点信息,颜色信息,法线信息,纹理坐标信息和索引信息等等;glVertexAttribPointer调用后,就相当于把多个VBO丢进指定的瓶子(在最近一次代码中创建的VAO)中装起来了,之后瓶子的瓶盖是紧紧盖着没打开状态,调用glEnableVertexAttribArray(其参数和glVertexAttribPointer函数的第一个参数相同,具体参考《glVertexAttribPointer第1个参数及着色器中的location的作用》)后,瓶盖就打开了,然后调用诸如:glDrawArrays、glDrawElements、glDrawElementsBaseVertex等绘图函数后,就相当于瓶子中存放的多个VBO中的数据都出来了,一次性并发送到GPU进行绘制。采用VBO、VAO节省了绘制多个物体时 CPU 与 GPU 之间的通讯时间,提高了渲染性能。

需要特别注意的是,在stackoverflow发现了这句话,如果你的VAO不能绘制出东西:
If you're on OpenGL-4 core, you need to create a VAO first, otherwise nothing will draw

注: VAO和VBO都是用来存储顶点信息的,并把这些信息送入顶点着色器。至于什么是顶点和顶点着色器,这里就不多说了,不了解的读者可自行优快云。

VBOBBuffer之意,用来存储顶点数据;VAOAArray,但我认为理解为 Attribute(属性) 之意更好,意思是 Buffer(VBO)的属性

即,我们用VBO来存储数据,而用VAO来告诉计算机这些数据分别有什么属性、起什么作用。

一、VBO的作用

VBO是 CPUGPU 之间传递信息的桥梁,我们把数据存入VBO(这一步在CPU执行),然后VBO会自动把数据送入GPU

送入GPU这一步,不需任何人为操作,用户只负责往VBO中存入数据就可以了。如下:

但是,对GPU来说,VBO中存的就只是一堆数字而已,要怎么解释它们呢?这就要用到VAO

二、VAO的作用

VBO是为了向GPU传递顶点数据,那么VAO就是为了向GPU解释顶点数据。有的读者会奇怪,顶点数据无非是一个个三维坐标,三个为一组,传就传了,为什么还需要解释呢?那么我们来看下面这个VBO中的数据:

float buffer = {
   	//顶点坐标(3个一组)              //顶点颜色(3个一组)              //纹理坐标(2个一组)
    0.5f,  0.5f, 0.0f,                1.0f, 0.0f, 0.0f,               1.0f, 1.0f,
    0.5f, -0.5f, 0.0f,                0.0f, 1.0f, 0.0f,               1.0f, 0.0f,
   -0.5f, -0.5f, 0.0f,                0.0f, 0.0f, 1.0f,               0.0f, 0.0f,
   -0.5f,  0.5f, 0.0f,                1.0f, 1.0f, 0.0f,               0.0f, 1.0f
};
//以上数据仅为本博文编造,不具有实操意义

由以上数据可见,顶点数据并非只是三个为一组的三维坐标!

如果我们向VBO中传入了以上buffer,并且VBO把它们送入了GPU。

然而,顶点着色器不知道该如何解释这些数字,到底是把它们3个一组,还是先3个一组、后2个一组,或者是3个、2个、3个?

GPU并不知道。

这就需要VAO的参与了,它负责告诉GPU,VBO中的信息到底该以几个为一组。如下这一段程序,就是把对VBO中数据的描述存到了一个VAO中:

	//vertex coord
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    // texture coord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);

上面程序看似有三段(glVertexAttribPointer() + glEnableVertexAttribArray()为一段),但它们都存在同一个VAO里。(这里就不介绍glVertexAttribPointer()glEnableVertexAttribArray()函数的作用了,不懂的读者可以上网去查。)

在一些复杂的OpenGL程序中,VBO可能会有多个,但VAO只有一个。那么,如何用一个VAO来解释多个VBO呢?

三、一个VAO和多个VBO

在OpenGL程序中,VBO可能会有多个,但VAO只有一个。那么,如何用一个VAO来解释多个VBO呢?

这就涉及到OpenGL上下文知识:一个完整的OpenGL程序相当于一个容器,我们在用到VAOVBO时,需要先绑定(Bind操作)、再使用,没有经过绑定的VAO/VBO是不起作用的。

一个VAO和多个VBO之间的关系大致如下所示:

那么,根据一个VAO与多个VBO的关系,以及OpenGL上下文(绑定)的知识,用一个VAO来解释多个VBO的操作流程就如下:

首先,绑定VAO,以告知OpenGL程序该使用这个VAO来对VBO做出解释。

然后,绑定第一个VBO,向这个VBO中写入数据,告知VAO该如何解释这个VBO的信息;
然后,解绑这个VBO。

然后,绑定第二个VBO,向这个VBO中写入数据,并在VAO中保存该如何解释这个VBO的信息;
然后,解绑这个VBO。
……

如下一段程序:

	unsigned int VBO[2], VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(2, VBO);

//=绑定VAO=======
glBindVertexArray(VAO);
//===============================================================

//=绑定第一个VBO======
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
//===============================================================

// 向VBO填充数据

glBufferData(GL_ARRAY_BUFFER, sphereVertices.size() * sizeof(float), &sphereVertices[0], GL_STATIC_DRAW); //向第一个VBO中写入数据

//告知VAO该如何解释第一个VBO的信息=
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 sizeof(float), (void)0);
glEnableVertexAttribArray(0);
//===============================================================

//=解绑第一个VBO=====
glBindBuffer(GL_ARRAY_BUFFER, 0);
//===============================================================

//=绑定第二个VBO======
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
//===============================================================

// 向VBO填充数据 

glBufferData(GL_ARRAY_BUFFER, sizeof(texVertrices), texVertrices, GL_STATIC_DRAW);//向第二个VBO中写入数据

//告知VAO该如何解释第二个VBO的信息=
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 sizeof(float), (void)0);
glEnableVertexAttribArray(1);
//=============================================================

<think>我们正在讨论OpenGL中的VAOVBO、EBO和Program之间的关系及使用方法。根据引用[1]、[2]、[3]、[4]提供的信息,我们可以总结如下:1.**VBO(VertexBufferObject)**:用于存储顶点数据的缓冲对象。它可以存储顶点的位置、颜色、纹理坐标等属性。在渲染时,VBO将顶点数据从CPU传输到GPU,从而减少数据传输开销并提高渲染性能。-使用方法(参考引用[3]):```cpp//创建VBOunsignedintVBO;glGenBuffers(1,&VBO);//绑定VBOglBindBuffer(GL_ARRAY_BUFFER,VBO);//将顶点数据复制到缓冲中glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);```2.**EBO(ElementBufferObject)**:也称为IBO(IndexBufferObject),用于存储索引数据。通过使用索引,我们可以避免重复存储相同的顶点,从而节省内存并提高效率。-使用方法(参考引用[4]):```cpp//创建EBOunsignedintEBO;glGenBuffers(1,&EBO);//绑定EBOglBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);//将索引数据复制到缓冲中glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);```3.**VAO(VertexArrayObject)**:用于存储顶点属性配置。它记录了对VBO的配置(如通过`glVertexAttribPointer`设置的属性指针)以及EBO的绑定状态。通过使用VAO,我们可以快速切换不同的顶点配置。-使用方法(结合引用[3]和[4]):```cpp//创建VAOunsignedintVAO;glGenVertexArrays(1,&VAO);//绑定VAOglBindVertexArray(VAO);//绑定VBO并配置顶点属性glBindBuffer(GL_ARRAY_BUFFER,VBO);glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);//设置顶点属性指针(例如位置属性)glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)0);glEnableVertexAttribArray(0);//绑定EBOglBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);//解绑VAO(在设置完成后解绑,以防止意外的修改)glBindVertexArray(0);```4.**Program**:着色器程序,由顶点着色器、片段着色器等链接而成。它定义了如何将顶点数据转换为屏幕上的像素。-使用方法(参考引用[3]):```cpp//创建着色器程序unsignedintshaderProgram=glCreateProgram();//附加着色器(顶点着色器和片段着色器等)glAttachShader(shaderProgram,vertexShader);glAttachShader(shaderProgram,fragmentShader);//链接着色器程序glLinkProgram(shaderProgram);//使用程序glUseProgram(shaderProgram);```###关系与交互方式:1.**VAOVBO/EBO**:-VAO保存了VBO的配置(即顶点属性的布局)和EBO的绑定状态。当需要绘制一个物体时,只需绑定对应的VAOOpenGL就会自动使用之前配置的VBO和EBO。-一个VAO可以关联多个VBO(例如一个用于位置,一个用于颜色),但只能关联一个EBO。2.**VBO与Program**:-VBO存储的顶点属性需要与着色器程序中的属性位置匹配。在着色器中,通常使用`layout(location=0)invec3aPos;`来声明属性位置,然后在设置VBO时使用`glVertexAttribPointer(0,...)`将属性位置0与当前绑定的VBO关联。3.**EBO与绘制调用**:-当使用EBO时,我们使用`glDrawElements`而不是`glDrawArrays`进行绘制。EBO中的索引会指向VBO中的顶点,从而决定顶点的绘制顺序。4.**交互流程**:-初始化阶段:-创建并配置VAO。-创建VBO并填充顶点数据(多个属性可能需要多个VBO)。-创建EBO并填充索引数据(可选)。-设置顶点属性指针(指定VBO中的数据如何解释)。-创建着色器程序并链接着色器。-渲染循环:-使用着色器程序(`glUseProgram`)。-绑定VAO(`glBindVertexArray`)。-绘制(使用`glDrawArrays`或`glDrawElements`)。-解绑VAO(可选,但良好的习惯是解绑)。###示例代码(综合):```cpp//初始化阶段unsignedintVAO,VBO,EBO,shaderProgram;//创建并绑定VAOglGenVertexArrays(1,&VAO);glBindVertexArray(VAO);//创建并绑定VBOglGenBuffers(1,&VBO);glBindBuffer(GL_ARRAY_BUFFER,VBO);glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);//设置顶点属性(位置属性,location=0)glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)0);glEnableVertexAttribArray(0);//创建并绑定EBO(如果使用索引)glGenBuffers(1,&EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);//创建着色器程序并链接着色器(省略着色器创建细节)shaderProgram=createShaderProgram(vertexShaderSource,fragmentShaderSource);//解绑VAOglBindVertexArray(0);//渲染循环中glUseProgram(shaderProgram);glBindVertexArray(VAO);//使用索引绘制glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);//或者不使用索引:glDrawArrays(GL_TRIANGLES,0,3);glBindVertexArray(0);//解绑```###总结:-**VAO**是顶点属性状态的封装,包含对VBO和EBO的引用。-**VBO**存储实际的顶点数据(位置、颜色等)。-**EBO**存储索引数据,用于索引顶点以实现重复使用。-**Program**是着色器程序,处理顶点的变换和像素的着色。它们共同协作,实现高效渲染。实际应用中,通常一个物体对应一个VAO(包含所需的VBO和EBO),而着色器程序则根据材质需求切换。---§§相关问题§§1.如何优化VAOVBO和EBO的内存管理?2.使用多个VBO时如何配置顶点属性?3.动态修改顶点数据时,应该使用哪种缓冲类型(如GL_STATIC_DRAW还是GL_DYNAMIC_DRAW)?4.如何调试OpenGLVAO的配置错误?</think> ### OpenGLVAOVBO、EBO和Program的关系与交互方式 #### 1. **核心组件定义** - **VBO(Vertex Buffer Object)** 存储顶点数据的GPU缓冲对象(位置、颜色、纹理坐标等)。 示例(引用[3]): ```cpp glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); ``` - **EBO(Element Buffer Object)** 存储顶点索引的缓冲对象,避免重复顶点数据(引用[4]): ```cpp glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); ``` - **VAO(Vertex Array Object)** 封装VBO/EBO的配置状态(属性指针、数据布局等),简化绘制调用。 - **Program(着色器程序)** 链接着色器(顶点/片段着色器)的执行单元,定义渲染管线行为。 #### 2. **组件交互关系** $$ \begin{array
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值