顶点数组对象(vao)
随着程序逐渐增大并且使用更多的模型,读者可能发现要在每个帧的多组顶点数组之间切换。根据你为每个顶点使用多少个顶点属性,像对glVetexPointer()这样的函数的调用次数可能变得很大。顶点数组对象捆绑了调用的集合,以设置顶点数组的状态。在初始化之后,可以通过单次调用在不同的顶点数组集合之间快速修改。
创建顶点数组对象标识:
void glGenVertexArrays(GLsizei n,GLuint *arrays);
返回n个当前未使用的名字,记录在数组arrays。
绑定顶点数组对象:
GLvoid glBindVertexArray(GLuint array);
该函数做3件事情:
1. 当使用的值array不是零并且是从glGenVertexArrays()返回的值时,创建一个新的顶点数组对象并且分配该名字。
2. 当绑定的之前创建的一个顶点数组对象时,该顶点数组对象变成活动的,这影响到存储在该对象中的顶点数组状态
3. 当绑定到一个为零的array值时,OpenGL停止使用顶点数组对象并且返回顶点数组的默认状态
注意:如果array不是之前从glGenVertexArrays()返回的值;如果它是glDeleteVertexArrays()已经释放的值;如果调用了任何一个gl*Pointer()函数来指定一个顶点数组,而在绑定一个非零顶点数组对象的时候,它没有和一个缓冲区对象关联起来,将会产生GL_INVALID_OPERATION错误。
删除顶点数组对象:
void glDeleteVertexArrays(GLsizei n,GLuint *arrays);
删除arrays中指定的n个顶点数组对象,使这些名字随后可以作为顶点数组重用。
确定一个特定的值是否可以表示一个已分配的顶点数组对象,可调用:
GLboolean glIsVertexArray(GLuint array);
如果array是之前glGenVertexArrays()产生的一个顶点数组对象的标识,且没有删除则返回GL_TRUE;如果array是0或者一个非顶点数组对象标识的非零值则返回GL_FALSE。
示例:展示使用顶点数组对象在两组顶点数组之间进行切换
#define BUFFER_OFFSET(offset) ((GLvoid *)NULL + offset)
#define NumberOf(array) (sizeof(array)/sizeof(array[0]))
typedef struct{
GLfloat x,y,z;
} vec3;
Typedef struct{
vec3 xlate;
GLfloat angle; //旋转角度
vec3 axis; //旋转轴
} XForm;
GLuint VAO[2]; //记录数组对象标识
GLenum PrimType[2]; //
GLsizei NumElements[2]; //记录索引长度
XForm xform[2]={
{{-2.0,0.0,0.0},0.0,{0.0,1.0,0.0}},
{{0.0,0.0,2.0},0.0,{1.0,0.0,0.0}}
};
GLfloat Angle = 0.0;
void ini{
glGenVertexArrays(2,VAO); //创建顶点数组对象标识
GLuint buffers[3]; //[0]记录顶点标识,[1]记录颜色标识,[2]记录索引标识
for(int n=0;n<2;n++){
GLfloat *verts;//顶点数组
GLfloat *colors;//颜色数组
GLubyte *indices;//索引数组
…
//初始化顶点、颜色、索引数组
…
glBindVertexArray(VAO[n]); //绑定顶点数组对象
glGenBuffers(3,buffers); //创建缓冲区对象标识
glBindBuffer(GL_ARRAY_BUFFER,buffers[0]); //绑定顶点缓冲区对象
glBufferData(GL_ARRAY_BUFFER,sizeof(verts),verts,GL_STATIC_DRAW);//填充顶点数据
glVertexPointer(3,GL_FLOAT,0,BUFFER_OFFSET(0));
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER,buffers[1]); //绑定颜色缓冲区对象
glBufferData(GL_ARRAY_BUFFER,sizeof(colors),colors,GL_STATIC_DRAW);//填充颜色数据
glColorPointer(3,GL_FLOAT,0,BUFFER_OFFSET(0));
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,buffers[2]); //绑定索引缓冲区对象
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices), indices,GL_STATIC_DRAW);//填充索引数据
PrimType[n] = GL_QUADS; //这里使用该值只是作为示例
NumElements[n] = NumberOf(indices);
}
glEnable(GL_DEPTH_TEST); //启用深度测试
}
void display(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕
glPushMatrix(); //将当前矩阵状态入栈,进行保存
glRotatef(Angle,0.0,1.0,0.0); //旋转
for(int i=0;i<2;i++){
glPushMatrix(); //矩阵入栈
//下面进行一些矩阵变换操作
glTranslatef(xform[i].xlate.x, xform[i].xlate.y, xform[i].xlate.z);
glRotatef(xform[i].angle, xform[i].axis.x, xform[i].axis.y, xform[i].axis.z);
//
glBindVertexArray(VAO[i]); //激活指定顶点数组对象
glDrawElements(PrimType[i], NumElements[i]); //绘制
glPopMatrix(); //矩阵出栈
}
glPopMatrix(); //矩阵出栈,恢复原先状态
…
}
附:对VBO和VAO的一些看法
VBO是Vertex Buffer Object(顶点缓冲区对象),VAO是Vertex Array Object(顶点数组对象)。
VBO其实就是显卡中的显存,为了提高渲染速度,可以将要绘制的顶点数据缓存在显存中,这样就不需要将要绘制的顶点数据重复从CPU发送到GPU,浪费带宽资源。
而VAO则是一个容器,可以包含多个VBO,它类似于以前的call list,由于它进一步将VBO容于其中,所以绘制效率将在VBO的基础上更进一步。