VAO和VBO API备忘

  • 概述

OpenGL从OpenGL 3.0开始将API分红了两种类型:即旧式的OpenGL(Legacy OpenGL)和新式的OpenGL(Core Profile),OpenGL3.3 的官方API文档完整地描述了新式的OpenGL API,旧式的OpenGL API能够在OpenGL 2.1中查看。html

对于OpenGL渲染的的第一站:也就是把顶点数据(包括顶点位置、顶点法线、顶点颜色、纹理坐标等)传入到OpenGL,旧式的OpenGL中有如下几种方式实现:数组

  1. 当即模式(glBegin/glEnd) 这种方式恐怕是学习OpenGL最先接触到的API吧,至少我是这样。不过很遗憾它已经被新式OpenGL抛弃了
  2. 顶点数组(VA:Vertex Array)相比当即模式减小了函数的调用开销。目前也被新式OpenGL抛弃
  3. 缓冲区对象(VBO:Vertex Buffer Object)相比较VA,将数据存储到服务器端(VA存储在客户端)。目前是新式OpenGL支持的惟一数据传入方式
  4. 显示列表(Display List)将若干条OpenGL命令编译好,直接由显卡调用。因为编译好的模块没法修改,丧失了灵活性。也被新式OpenGL抛弃

此外VBO在Legacy OpenGL中和Core Profile OpenGL中的使用也有着不一样的方式。服务器

  • VA API

(1) 开启和关闭VAide

//开启和关闭客户端状态  

void glEnableClientState(GLenum cap);  

void glDisableClientState(GLenum cap);  

  

//参数cap取值  

GL_VERTEX_ARRAY                 //顶点位置        

GL_COLOR_ARRAY                  //顶点颜色  

GL_EDGE_FLAG_ARRAY              //顶点边界线标识  

GL_FOG_COORD_ARRAY              //顶点雾坐标  

GL_INDEX_ARRAY                  //顶点索引  

GL_NORMAL_ARRAY                 //顶点法线  

GL_SECONDARY_COLOR_ARRAY            //顶点辅助颜色  

GL_TEXTURE_COORD_ARRAY              //顶点纹理坐标  

(2) 设置数据到顶点函数

//////////////////////////////////////////////////////////////////////////  
//如下是一系列设置顶点数据的API  
//参数取值(默认形参表明OpenGL中默认值):  
// size   描述数据的维度(2D\3D)   
// type   描述每一个数据的类型   
// stride 描述每一个顶点数据的跨度  
//pointer 指向实际数据  
  
  
//设置顶点位置数据  
void glVertexPointer( GLint size=4,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//设置顶点颜色数据  
void glColorPointer( GLint size=4,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//设置顶点边界线标识数据  
void glEdgeFlagPointer( GLsizei stride=0,  const GLvoid *pointer=0);   
//设置顶点雾坐标数据  
void glFogCoordPointer( GLenum type=GL_FLOAT, GLsizei stride=0,  GLvoid *pointer=0);   
//设置顶点索引数据  
void glIndexPointer( GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//设置顶点法线数据  
void glNormalPointer( GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//设置顶点辅助颜色数据  
void glSecondaryColorPointer( GLint size=3,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   
//设置顶点纹理坐标数据  
void glTexCoordPointer( GLint size=4,  GLenum type=GL_FLOAT,  GLsizei stride=0,  const GLvoid *pointer=0);   

(3) VA绘制学习

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
//经过设置好的顶点数据进行绘制:可使用下列API进行绘制  
  
void glDrawArrays( GLenum mode,  GLint first,  GLsizei count);  
//参数描述  
//mode    绘制几何图元类型(GL_POINTS, GL_LINE_STRIP, GL_TRIANGLE等)  
//first      顶点数据的开始索引位置(对应(2)中pointer)  
//count    须要渲染的顶点数据数量  
  
void glDrawElements( GLenum mode,  GLsizei count,  GLenum type,  const GLvoid *indices);   
//参数描述  
//mode     绘制几何图元类型(GL_POINTS, GL_LINE_STRIP, GL_TRIANGLE等)  
//count     第四个参数indices中参与渲染的索引数量  
//type       第四个参数indices的数据类型  
//indices  须要额外传入的一个索引数组指针  
  
void glDrawRangeElements( GLenum mode, GLuint start,  GLuint end,  GLsizei count,  GLenum type,  const GLvoid * indices);  
//参数描述(与glDrawElements一致但多出两个参数)  
//mode     绘制几何图元类型(GL_POINTS, GL_LINE_STRIP, GL_TRIANGLE等)  
//count     第四个参数indices中参与渲染的索引数量  
//type       第四个参数indices的数据类型  
//indices  须要额外传入的一个索引数组指针  
//start       索引数组indices中的最小值索引值  
//end        索引数组indices中的最大值索引值  

以上就是VA所涉及到的基本API,使用过程以下优化

首先开启客户端VertexArray状态,接着绑定数据到顶点状态,最后进行绘制:ui

static float vertices[][3] = {  
    1.0f, 0.0f, 0.0f,  
    0.0f, 1.0f, 0.0f,  
    -1.0f, 0.0f, 0.0f  
};  
  
static float colors[][4] = {  
    1.0f, 0.0f, 0.0f, 1.0f,  
    0.0f, 1.0f, 0.0f, 1.0f,  
    0.0f, 0.0f, 1.0f, 1.0f  
};  
  
//绘制函数  
void renderScene()  
{  
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  
    glMatrixMode(GL_MODELVIEW);  
    glLoadIdentity();  
    glTranslatef(0.0f, 0.0f, -5.0f);  
      
    //////////////////////////////////////////////////////////////////////////  
    //开启VA状态  
    glEnableClientState(GL_VERTEX_ARRAY);  
    glEnableClientState(GL_COLOR_ARRAY);  
    //绑定数据  
    glVertexPointer(3, GL_FLOAT, 0, vertices);  
    glColorPointer(4, GL_FLOAT, 0, colors);  
    //绘制  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
    //关闭VA状态  
    glDisableClientState(GL_VERTEX_ARRAY);  
    glDisableClientState(GL_COLOR_ARRAY);  
    //////////////////////////////////////////////////////////////////////////  
}     
  • VBO API

前文描述了VBO在Legacy 和Core Profile中有两种不一样的方式:spa

  • Legacy OpenGL VBO API

(1)建立(初始化)VBO对象指针

//建立VBO的API  
//////////////////////////////////////////////////////////////////////////  
void glGenBuffers( GLsizei   n, GLuint *   buffers);   
//生成缓冲区ID  
//参数描述  
// n            生成ID数量  
// buffers 存储缓冲区ID的数组  
  
void glBindBuffer( GLenum   target,  GLuint   buffer);   
//设置缓冲区为当前操做的缓冲区,而且设置缓冲区的类型  
//参数描述  
//target            缓冲区类型(包括4种)  
GL_ARRAY_BUFFER  
GL_ELEMENT_ARRAY_BUFFER  
GL_PIXEL_PACK_BUFFER  
GL_PIXEL_UNPACK_BUFFER  
//buffer        使用glGenBuffers生成的缓冲区ID  
  
void glBufferData( GLenum   target,  GLsizeiptr   size,  const GLvoid *   data,  GLenum   usage);   
//为缓冲区设置数据  
//参数描述  
//target            缓冲区类型(包括4种)  
GL_ARRAY_BUFFER  
GL_ELEMENT_ARRAY_BUFFER  
GL_PIXEL_PACK_BUFFER  
GL_PIXEL_UNPACK_BUFFER  
//size             缓冲区大小(多少字节)  
//data         实际指向数组的指针  
//usage        当前缓冲区的使用模式[一种提示用来优化之用](取值以下9种)  
GL_STREAM_DRAW  
GL_STREAM_READ  
GL_STREAM_COPY  
GL_STATIC_DRAW  
GL_STATIC_READ  
GL_STATIC_COPY  
GL_DYNAMIC_DRAW  
GL_DYNAMIC_READ  
GL_DYNAMIC_COPY  

(2)开启/关闭VBO

当建立完VBO以后,咱们并不知道VBO中存储的是顶点位置、仍是顶点颜色或者是顶点法线,因而使用下面的API来描述某一个VBO中究竟是顶点什么方面的数据

//////////////////////////////////////////////////////////////////////////  
//开启关闭VBO状态  
void glEnableClientState(GLenum cap);  
void glDisableClientState(GLenum cap);  
//参考VA中的描述  
  
  
void glVertexPointer( GLint   size,  GLenum   type,  GLsizei   stride,  const GLvoid *   pointer);   
//参考VA中的描述:  
//可是pointer参数有所区别:  
//当使用了glBindBuffer将GL_ARRAY_BUFFER绑定到一个非0的VBO ID上以后,这时候的pointer  
//表明的是一个VBO ID对象中实际数据(glBufferData中data参数)的偏移值,也就是说这时候pointer  
//并非一个指针,仅仅是一个偏移值。同时GL_ARRAY_BUFFER_BINDING这个状态会保存在客户端  
  
//综合上面VA中的解释:能够知道这个glVertexPointer中的pointer两种含义:  
//(1)当glBindBuffer绑定到非0的ID时:  
//pointer表明偏移,此时客户端glClientState中会保存GL_ARRAY_BUFFER_BINDING暗示如今在用VBO  
//(2)当glBindBuffer绑定到0的ID时:  
//pointer表明一个指向数组的指针,此时客户端glClientState中不会保存GL_ARRAY_BUFFER_BINDING暗示如今在用VA  
  
//同理,对于VA中用到的一系列函数都有一样的解释,再也不赘述:  
glColorPointer  
glEdgeFlagPointer  
glFogCoordPointer  
glIndexPointer  
glNormalPointer  
glSecondaryColorPointer  
glTexCoordPointer 

 

(3)VBO绘制

//////////////////////////////////////////////////////////////////////////  
//绘制VBO  
  
void glDrawArrays( GLenum mode,  GLint first,  GLsizei count);  
//参考VA中描述  
//所不一样的是数据如今在缓冲区之中,而不是VA所指向的pointer数组中了  
  
void glDrawElements( GLenum mode,  GLsizei count,  GLenum type,  const GLvoid *indices);  
//参考VA中描述  
//所不一样的是indices设置为NULL  
//由于缓冲区类型有一种是GL_ELEMENT_ARRAY_BUFFER(查看glGenBuffers中类型描述)  
//咱们会把indices索引值存储在GL_ELEMENT_ARRAY_BUFFER类型的Buffer中,不须要额外的数组了  
//直接用两个缓冲区便可 

 

以上即是旧式OpenGL中实现VBO的API,使用过程以下:

首先建立缓冲区并设置缓冲区类型和填满缓冲区,接着指定缓冲里面是什么(到底存的是位置仍是颜色或者是法线),最后绘制

static float vertices[][3] = {  
    1.0f, 0.0f, 0.0f,  
    0.0f, 1.0f, 0.0f,  
    -1.0f, 0.0f, 0.0f  
};  
  
static float colors[][4] = {  
    1.0f, 0.0f, 0.0f, 1.0f,  
    0.0f, 1.0f, 0.0f, 1.0f,  
    0.0f, 0.0f, 1.0f, 1.0f  
};  
  
void renderScene()  
{  
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  
    glMatrixMode(GL_MODELVIEW);  
    glLoadIdentity();  
    glTranslatef(0.0f, 0.0f, -5.0f);  
      
    //////////////////////////////////////////////////////////////////////////  
    //建立VBO(通常放在初始化函数中)  
    GLuint vertexBufferID, colorBufferID;  
    glGenBuffers(1, &vertexBufferID);  
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  
    glGenBuffers(1, &colorBufferID);  
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferID);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);  
  
    //开启VBO模式并设置VBO该怎么解析  
    glEnableClientState(GL_VERTEX_ARRAY);  
    glEnableClientState(GL_COLOR_ARRAY);  
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);  
    glVertexPointer(3, GL_FLOAT, 0, 0);  
    glBindBuffer(GL_ARRAY_BUFFER, colorBufferID);  
    glColorPointer(4, GL_FLOAT, 0, 0);  
  
    //绘制  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
  
    //绘制完成以后关闭VBO状态  
    //将glBindBuffer设置为0,使得后续glVertexPointer相似函数起到VA的做用  
    glDisableClientState(GL_VERTEX_ARRAY);  
    glDisableClientState(GL_COLOR_ARRAY);  
    glBindBuffer(GL_ARRAY_BUFFER, 0);  
    //////////////////////////////////////////////////////////////////////////  
}     
  • Core Profile VBO

(1)建立VBO

参考Legacy VBO中内容,两者是同样的

(2)开启/关闭VBO

//////////////////////////////////////////////////////////////////////////  
void glEnableVertexAttribArray( GLuint   index);   
void glDisableVertexAttribArray( GLuint   index);   
//开启和关闭一个通用的缓冲区对象  
//参数描述  
//index 缓冲区对象索引值  
  
void glVertexAttribPointer( GLuint   index,    
                                              GLint            size,    
                                              GLenum           type,    
                                              GLboolean        normalized,    
                                              GLsizei          stride,    
                                              const GLvoid *   pointer  
                                              );   
//设置如何解析缓冲区中的数据  
//参数描述  
//index             当前操做的缓冲区对象索引值  
//size              数据的维度是2D/3D/4D  
//type              数据类型 GL_INT /GL_FLOAT  
//normalized    是否已经单位化  
//stride                数据间距  
//pointer           参考Legacy VBO中解释(在glBindBuffer未开启时是指针开启后是索引值) 

 

(3)绘制

参考Legacy VBO中内容,两者是同样的

以上即是Core Profile 下的VBO,使用方式以下:

首先建立VBO对象,接着开启VBO而且用来讲明VBO中数据是什么样组织的(可是并无说明数据是顶点位置、顶点颜色仍是法线),这一点与 Legacy VBO不一样,由于它的说明部分(哪一个VBO存储着位置、哪一个VBO存储着颜色、哪一个VBO存储着法线等)是在着色语言Shader中指明的,最后仍是使用 一样的API绘制:

//////////////////////////////////////////////////////////////////////////  
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,  
0.5f, 0.0f, 0.0f,  
0.0f, 0.5f, 0.0f };  
  
GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,  
0.0f, 1.0f, 0.0f, 1.0f,  
0.0f, 0.0f, 1.0f, 1.0f };  
//////////////////////////////////////////////////////////////////////////  
  
void RenderWidget::paintGL()  
{  
    //////////////////////////////////////////////////////////////////////////  
    //生成VBO  
    glGenBuffers(1, &_vertexBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVerts), vVerts, GL_STATIC_DRAW);  
  
    glGenBuffers(1, &_colorBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vColors), vColors, GL_STATIC_DRAW);  
  
    setShaders();  
  
    //开启VBO并设置VBO里面存储的数据模式  
    glEnableVertexAttribArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glVertexAttribPointer(  
        0,  
        3,  
        GL_FLOAT,  
        GL_FALSE,   
        0,  
        (void*)0  
        );  
    glEnableVertexAttribArray(1);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glVertexAttribPointer(  
        1,  
        4,  
        GL_FLOAT,  
        GL_FALSE,  
        0,  
        (void*)0  
        );  
  
    //绘制VBO  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
  
    //关闭VBO  
    glDisableVertexAttribArray(1);  
    glDisableVertexAttribArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, 0);  
}  

Shader部分

顶点Shader

#version 330  
layout(location = 0) in vec4 vertex;  
layout(location = 1) in vec4 color;  
out vec4 inFragColor;  
  
void main( void )  
{  
    gl_Position = vertex;  
    inFragColor = color;  
}  

片元Shader

#version 330  
in vec4 inFragColor;  
out vec4 outFragColor;  
  
void main( void )  
{  
    outFragColor = inFragColor;  
}  


经过顶点Shader能够知道索引值为0的VBO解释为顶点位置,它传给gl_Position,索引值为1的VBO解释为顶点的颜色

  • VAO API

VAO的介绍能够参考 <<AB是一家?VAO与VBO>>

(1)初始化

//////////////////////////////////////////////////////////////////////////  
//初始化VAO  
  
void glGenVertexArrays(GLsizei n,  GLuint *arrays);  
//建立VAO ID  
//参数描述  
//n                 产生VAO ID的数量  
//arrays                    保存VAO ID的数组  
  
void glBindVertexArray(GLuint array);  
//设置当前操做的VAO对象  
//参数描述  
//array              VAO的ID  


(2)开启/关闭VAO

VAO的开启当使用glBindVertexArray时自动开启,使用glBindVertexArray(0),传入一个0值能够视为将VAO关闭

设置VAO的过程就是调用VBO中(2)的过程

(3) 绘制

同VBO中绘制(绘制以前先启用VAO)

以上就是VAO涉及到的API,使用过程以下:

首先建立VAO,接着设置VAO(在调用VBO的数据设置过程当中VAO自动完成了设置),最后开启VAO并绘制:

//////////////////////////////////////////////////////////////////////////  
GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,  
0.5f, 0.0f, 0.0f,  
0.0f, 0.5f, 0.0f };  
  
GLfloat vColors [] = { 1.0f, 0.0f, 0.0f, 1.0f,  
0.0f, 1.0f, 0.0f, 1.0f,  
0.0f, 0.0f, 1.0f, 1.0f };  
//////////////////////////////////////////////////////////////////////////  
  
void RenderWidget::paintGL()  
{  
    //////////////////////////////////////////////////////////////////////////  
    //生成VBO  
    glGenBuffers(1, &_vertexBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVerts), vVerts, GL_STATIC_DRAW);  
  
    glGenBuffers(1, &_colorBuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(vColors), vColors, GL_STATIC_DRAW);  
  
    setShaders();  
  
      
    //初始化VAO  
    GLuint vaoBuffer;  
    glGenVertexArrays(1, &vaoBuffer);  
    glBindVertexArray(vaoBuffer);  
  
    //经过设置VBO里面存储的数据模式完成VAO设置  
    glEnableVertexAttribArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);  
    glVertexAttribPointer(  
        0,  
        3,  
        GL_FLOAT,  
        GL_FALSE,   
        0,  
        (void*)0  
        );  
    glEnableVertexAttribArray(1);  
    glBindBuffer(GL_ARRAY_BUFFER, _colorBuffer);  
    glVertexAttribPointer(  
        1,  
        4,  
        GL_FLOAT,  
        GL_FALSE,  
        0,  
        (void*)0  
        );  
    glBindVertexArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, 0);  
  
    //////////////////////////////////////////////////////////////////////////  
    //上述全部的代码在初始化函数中调用  
    //只有绘制代码在渲染函数中调用  
    //使用VAO绘制  
    glBindVertexArray(vaoBuffer);  
    glDrawArrays(GL_TRIANGLES, 0, 3);  
    glBindVertexArray(0);  
}
相关文章
相关标签/搜索