VAO和VBO API备忘

Wesley13
• 阅读 596
  • 概述

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_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);  
}
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
2年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
5个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这