·顶点数组对象(Vertex Array Object,VAO)

顶点数组对象(Vertex Array Object,VAO),用来记录顶点的信息,如:位置、数据格式、纹理坐标等。使用VAO的好处是:在配置绘制对象的顶点属性时,你只需要配置一次(VAO会自动记录你的设置),想要绘制对象时,绑定相应的VAO就可以了。这使得在不同绘制对象之间切换变得非常简单,只需要绑定相应的VAO就行了。创建和使用VAO的代码如下:

//创建1个VAO并绑定
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

//do something 设置顶点信息

//这里的x根据自己的顶点信息来设置
glVertexAttribPointer(0, x, GL_FLOAT, GL_FALSE, x * sizeof(float), (void*)0);
//这个函数的调用位置比较灵活,在绑定后且解绑前且绘制前调用即可。
glEnableVertexAttribArray(0);
//解绑VAO
glBindVertexArray(0);

//do something

//删除无用对象,通常在程序退出前调用
glDeleteVertexArrays(1, &VAO);

注意:
1.OpenGL的核心模式要求我们使用VAO,如果我们绑定VAO失败,核心模式下的OpenGL会拒绝绘制任何东西;
2.考虑到性能的原因,顶点着色器的属性变量默认是关闭的,所以顶点的属性数据在顶点着色器中是不可见的,即使数据已经上传到GPU中。当调用glEnableVertexAttribArray后,顶点着色器中才可以访问顶点的属性数据。调用glVertexAttribPointer只是建立CPU和GPU之间的逻辑连接,把CPU中的数据上传至GPU。但是,顶点着色器能否读取、使用数据,由是否调用glEnableVertexAttribArray决定。

·顶点缓冲对象(Vertex Buffer Object,VBO)

顶点缓冲对象(Vertex Buffer Object,VBO),本质上是GPU存储空间里的一块缓存区。这块缓存区有它的"名字"(就是调用创建函数的返回值),OpenGL在GPU的某处记录着这个"名字"和"名字"对应的显存地址。使用VBO的好处是:我们可以一次性地发送一大批数据到GPU上,而不是多次发送。从CPU把数据发送到GPU相对较慢,所以我们一次要发送尽可能多的数据。当数据发送到GPU的内存后,顶点着色器几乎能立即访问到顶点数据,这是大大提高了效率。创建和使用VAO的代码如下:

//通常在前面会先创建一个数据数组和创建、绑定一个VAO

//创建1个VBO并绑定
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//这里的vertices是事先定义好的数据数组
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

//解绑VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);

//do something

//删除无用对象,通常在程序退出前调用
glDeleteBuffers(1, &VBO);

注意:
1.在VBO的创建和绑定数据阶段,不知道也没必要知道它存的是数据的意义(是坐标、颜色还是其他),数据的类型是在渲染阶段的定义的。

·索引缓冲对象(Element Buffer Object,EBO)

索引缓冲对象(Element Buffer Object,EBO或称Index Buffer Object,IBO),EBO实际上是一个顶点信息的索引表,它首先把顶点信息按0-x(x是顶点数 - 1)编号,然后创建一个由若干个i(i的取值范围为0-x,数量视情况而定)组成的索引表。在绘制图像的时候,会根据这个索引表来绘制。使用VBO的好处是:如果有些绘制的点是相同的,你只需要定义一次,然后在索引表中引用,不需要绘制几次定义几次。创建和使用EBO的代码如下:

//通常在前面会完成顶点数据的定义并绑定到一个VBO,而且会创建一个索引表数组

//创建1个索引缓冲对象并绑定
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
//这里的indices是事先定义好的索引表数组
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

//do something

//绘制时
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
//这里的param1-4,根据实际情况调用
glDrawElements(param1, param2, param3, param4);

//删除无用对象,通常在程序退出前调用
glDeleteBuffers(1, &EBO);
10-03 19:20