我们的顶点数组在CPU端的内存里是以数组的形式存在,想要GPU去绘制三角形,那么需要将这些数据传输给GPU。那这些数据在显存端是怎么存储的呢?VBO上场了,它代表GPU上的一段存储空间对象,表现为一个unsigned int类型的变量,GPU端内存对象的一个ID编号、地址、大小。一个VBO对象既代表了GPU端的一段区域。
VBO的创建
VBO的销毁
// 1 创建一个vbo /*还没有真能正分配显存*/
GLuint vbo = 0;
GL_CALL(glGenBuffers(1, &vbo));
// 2 销毁一个vbo
GL_CALL(glDeleteBuffers(1, &vbo));
// 3 创建n个vbo
GLuint vboArr[] = { 0,0,0 };
GL_CALL(glGenBuffers(3, vboArr));
// 4 销毁n个vbo
GL_CALL(glDeleteBuffers(sizeof(vboArr), vboArr));
VBO绑定+数据更新
VBO绑定
VBO填入数据
该函数会对现存进行销毁重建操作,需要谨慎调用。
多属性数据
通常,一个三角形不仅仅有位置信息,也可能存在颜色、法向、纹理等数据。 这时候我们该怎么存储呢?
针对以上问题,我们能想到创建两个vbo去存放。
还有一种办法是将两种数据存储在一个buffer中,且数据是交叉的。
不管使用哪种方式,我们都应该对数据进行描述,以便GPU对数据进行合理的使用,否则在GPU端也不知道那几个数字代表一个顶点。所以我们需要告诉GPU每个顶点的布局。
什么是VAO?
VAO用来记录每种数据的描述信息的。每个描述信息都描述了某个信息,位置或者颜色。
对于位置颜色在同一个vbo时,我们应该如何去描述,让GPU能够理解这对数据?
对于一个vb中仅有位置数据时,只需要知道:
- 每个顶点有几个数据
- 每个数字的类型
- 每个顶点数据的步长
- 该属性存储在几号vbo(1号)
只要知道这些数据即可准确的描述顶点数据。
对于一个vbo 中既有位置信息又有其他属性时
我们凭借以上信息不能准确地拿到某个属性数据,比如下图中的颜色信息。
位置数据处于每组数据的开头,颜色信息处于每组数据的其中,针对每组数据,位置信息存在一个固定的偏移量,这个偏移量就是每个顶点数据的大小。
综上,总结如下:
VAO的创建与删除
VAO加入VBO描述
DEMO1(顶点数据独立存放)
void prepareSingleBuffer()
{
float positions[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
float colors[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
// 1 生成一个vbo
GLuint vboPos = 0, vboColor = 0;
GL_CALL(glGenBuffers(1, &vboPos));
GL_CALL(glGenBuffers(1, &vboColor));
// 2 绑定当前vbo,到opengl状态机的当前vbo插槽
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));
// 创建及绑定vao
GLuint vaoPos = 0;
GL_CALL(glGenVertexArrays(1, &vaoPos));
GL_CALL(glBindVertexArray(vaoPos));
// 描述为指数型
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboPos)); // 只有绑定了vbo,下面的属性描述才会有当前vbo有关系
GL_CALL(glEnableVertexAttribArray(0));
GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (GLvoid*)0));
// 创建及绑定vao
GLuint vaoColor = 0;
GL_CALL(glGenVertexArrays(1, &vaoColor));
GL_CALL(glBindVertexArray(vaoColor));
// 描述为指数型
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vboColor)); // 只有绑定了vbo,下面的属性描述才会有当前vbo有关系
GL_CALL(glEnableVertexAttribArray(1));
GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (GLvoid*)0));
GL_CALL(glBindVertexArray(0));
}
DEMO(顶点数据混合存放)
void prepareInterleavedBuffer()
{
float vertices[] = {
-0.5f, -0.5f, 0.0f,1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f,0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f,0.0f, 0.0f, 1.0f
};
GLuint vbo = 0;
GL_CALL(glGenBuffers(1, &vbo));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));
GLuint vao = 0;
GL_CALL(glGenVertexArrays(1, &vao));
GL_CALL(glBindVertexArray(vao));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
GL_CALL(glEnableVertexAttribArray(0));
GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)0));
GL_CALL(glEnableVertexAttribArray(1));
GL_CALL(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT))));
GL_CALL(glBindVertexArray(0));
}