VA、VAO和VBO API备忘

来源:互联网 发布:科比詹姆斯知乎 编辑:程序博客网 时间:2024/05/22 17:46
  • 缘起

接触OpenGL有一段时间了,大多数时候都是使用OSG在进行开发,前几天同事问起OpenGL中VBO的相关内容,才发现很多OpenGL的知识生疏了。这两天花了点时间重新复习了一下与VBO相关的内容,记录在此以备忘。文中大部分内容来自网络。

  • 概述

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

对于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) 开启和关闭VA

//开启和关闭客户端状态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状态,接着绑定数据到顶点状态,最后进行绘制:

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中有两种不同的方式:

  • 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_BUFFERGL_ELEMENT_ARRAY_BUFFERGL_PIXEL_PACK_BUFFERGL_PIXEL_UNPACK_BUFFER//buffer        使用glGenBuffers生成的缓冲区IDvoid glBufferData( GLenum   target,  GLsizeiptr   size,  const GLvoid *   data,  GLenum   usage); //为缓冲区设置数据//参数描述//target缓冲区类型(包括4种)GL_ARRAY_BUFFERGL_ELEMENT_ARRAY_BUFFERGL_PIXEL_PACK_BUFFERGL_PIXEL_UNPACK_BUFFER//size             缓冲区大小(多少字节)//data   实际指向数组的指针//usage   当前缓冲区的使用模式[一种提示用来优化之用](取值如下9种)GL_STREAM_DRAWGL_STREAM_READGL_STREAM_COPYGL_STATIC_DRAWGL_STATIC_READGL_STATIC_COPYGL_DYNAMIC_DRAWGL_DYNAMIC_READGL_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中用到的一系列函数都有同样的解释,不再赘述:glColorPointerglEdgeFlagPointerglFogCoordPointerglIndexPointerglNormalPointerglSecondaryColorPointerglTexCoordPointer
(3)VBO绘制
////////////////////////////////////////////////////////////////////////////绘制VBOvoid 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(){////////////////////////////////////////////////////////////////////////////生成VBOglGenBuffers(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);//绘制VBOglDrawArrays(GL_TRIANGLES, 0, 3);//关闭VBOglDisableVertexAttribArray(1);glDisableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0);}
Shader部分

顶点Shader

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

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

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

  • VAO API

VAO的介绍可以参考 <<AB是一家?VAO与VBO>>

(1)初始化

////////////////////////////////////////////////////////////////////////////初始化VAOvoid 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(){////////////////////////////////////////////////////////////////////////////生成VBOglGenBuffers(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();    //初始化VAOGLuint 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);}





0 0
原创粉丝点击