当前位置: 首页 > news >正文

OpenGL-ES 学习(12) ---- VBO EBO VAO

目录

      • VBO 定义
      • VBO 创建
      • 统一VertexData
      • 使用 VBO 绘制
      • VAO

VBO 定义

VBO(Vertex Buffer Object) 是指顶点缓冲区对象,而 EBO(Element Buffer Object)是指图元索引缓冲区对象,VBOEBO实际上是同一类 buffer 按照用途的不同称呼
OpenGL-ES2.0 编程中,用于绘制的顶点数组数据首先保存在系统内存,在调用 glDrawArrays 或者 glDrawElements 等进行绘制的时候,需要将顶点数组数据从系统内存拷贝到显存,但是很多时候我们没必要在每次绘制的时候进行内存拷贝,如果可以在显存中缓存这些数据,就可以在很大程度上降低内存拷贝带来的开销

OpenGL-ES 支持的缓冲区对象包含:

  • 数组缓冲区对象 GL_ARRAY_BUFFER 指定数组缓冲区对象用于创建保存顶点数据的缓冲区对象
  • 元素数组缓冲区对象 GL_ELEMENT_ARRAY_BUFFER 用于创建保存图元索引的缓冲区对象
  • 统一变量缓冲区
  • 变换反馈缓冲区
  • 像素解包缓冲区
  • 像素包装缓冲器
  • 复制缓冲区

OpenGL-ES3.0 编程中,VBOEBO 的出现就是为了解决这个问题
VBOEBO 的作用就是在显存中提前开辟好一段内存,用于缓存顶点数据或者图元索引数据,从而避免每次绘制时 CPUGPU 之间的内存拷贝,改进渲染性能,降低内存带宽和功耗

  • GL_Array_Buffer 标志指定的缓冲区对象用于保存顶点数组
  • GL_Element_Array_Buffer 用于标识指定缓冲区对象用于保存图元索引

VBO 创建

VBO 创建的基本流程如下所示:

// 创建两个VBO
glGenBuffers(2, userData->vboIds);//绑定第一个VBO,拷贝顶点数组到显存
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
glBufferData(GL_ARRAY_BUFFER, vtxStride * numVertices, vtxBuf, GL_STATIC_DRAW);//绑定第二个VBO,拷贝图元索引的显存(EBO)
//可以认为图元索引也需要同时标志为(Element Buffer object)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort) * numIndices, indices, GL_STATIC_DRAW);

void glBufferData( GLenum target, GLsizeiptr size, const void * data, GLenum usage);
注意第二个和第三个参数:

  • GLsizeiptr size : 顶点存储区的实际大小(Bytes 为单位)
  • const void * data : 指向顶点存储区的指针

统一VertexData

前面章节三角形的 demo 中,Vertex Array 的值得单独设定的,但是 Vertex 的颜色是使用的固定值,这里可以给每个Vertex 单独设定颜色
drawWithoutVBO.png

顶点位置和顶点颜色可以放在同一个数组中,通过 glVertexAttribPointer 的起始位置和 stride 进行区分,示意如下:
stride.png

示例代码如下:(不使用 VBO 绘制)

#define VERTEX_POS_SIZE       3 // x, y and z
#define VERTEX_COLOR_SIZE     4 // r, g, b, and a#define VERTEX_POS_INDX       0
#define VERTEX_COLOR_INDX     1//代码起点,绘制函数
void Draw ( ESContext *esContext ) {UserData *userData = esContext->userData;// 3 vertices, with (x,y,z) ,(r, g, b, a) per-vertexGLfloat vertices[3 * ( VERTEX_POS_SIZE + VERTEX_COLOR_SIZE )] = {-0.5f,  0.5f, 0.0f,        // v01.0f,  0.0f, 0.0f, 1.0f,  // c0-1.0f, -0.5f, 0.0f,        // v10.0f,  1.0f, 0.0f, 1.0f,  // c10.0f, -0.5f, 0.0f,        // v20.0f,  0.0f, 1.0f, 1.0f,  // c2};// Index buffer dataGLushort indices[3] = { 0, 1, 2 };glViewport ( 0, 0, esContext->width, esContext->height );glClear ( GL_COLOR_BUFFER_BIT );glUseProgram ( userData->programObject );glUniform1f ( userData->offsetLoc, 0.0f );DrawPrimitiveWithoutVBOs ( vertices,sizeof ( GLfloat ) * ( VERTEX_POS_SIZE + VERTEX_COLOR_SIZE ),3, indices );}void DrawPrimitiveWithoutVBOs ( GLfloat *vertices,GLint vtxStride, GLint numIndices, GLushort *indices ){GLfloat   *vtxBuf = vertices;glBindBuffer ( GL_ARRAY_BUFFER, 0 );glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 );glEnableVertexAttribArray ( VERTEX_POS_INDX );glEnableVertexAttribArray ( VERTEX_COLOR_INDX );glVertexAttribPointer ( VERTEX_POS_INDX, VERTEX_POS_SIZE,GL_FLOAT, GL_FALSE, vtxStride,vtxBuf );vtxBuf += VERTEX_POS_SIZE;//注意vertex color 加上了偏移量glVertexAttribPointer ( VERTEX_COLOR_INDX,VERTEX_COLOR_SIZE, GL_FLOAT,GL_FALSE, vtxStride, vtxBuf );glDrawElements ( GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT,indices );glDisableVertexAttribArray ( VERTEX_POS_INDX );glDisableVertexAttribArray ( VERTEX_COLOR_INDX );}

使用 VBO 绘制

drawWithVBO.png

使用 VBO 绘制的参考代码如下:

#define VERTEX_POS_SIZE       3 // x, y and z
#define VERTEX_COLOR_SIZE     4 // r, g, b, and a#define VERTEX_POS_INDX       0
#define VERTEX_COLOR_INDX     1void Draw ( ESContext *esContext ) {UserData *userData = esContext->userData;// 3 vertices, with (x,y,z) ,(r, g, b, a) per-vertexGLfloat vertices[3 * ( VERTEX_POS_SIZE + VERTEX_COLOR_SIZE )] = {-0.5f,  0.5f, 0.0f,        // v01.0f,  0.0f, 0.0f, 1.0f,  // c0-1.0f, -0.5f, 0.0f,        // v10.0f,  1.0f, 0.0f, 1.0f,  // c10.0f, -0.5f, 0.0f,        // v20.0f,  0.0f, 1.0f, 1.0f,  // c2};// Index buffer dataGLushort indices[3] = { 0, 1, 2 };glViewport ( 0, 0, esContext->width, esContext->height );glClear ( GL_COLOR_BUFFER_BIT );glUseProgram ( userData->programObject );glUniform1f ( userData->offsetLoc, 0.0f );DrawPrimitiveWithVBOs(esContext, 4, vertices,sizeof(GLfloat) * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE),4, indices);}static void DrawPrimitiveWithVBOs(ESContext *esContext,GLint numVertices, GLfloat *vtxBuf,GLint vtxStride, GLint numIndices,GLushort *indices) {UserData *userData = esContext->userData;GLuint   offset = 0;// vboIds[0] - used to store vertex attribute data// vboIds[l] - used to store element indices (EBO)if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0){// Only allocate on the first drawglGenBuffers(2, userData->vboIds);glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);glBufferData(GL_ARRAY_BUFFER, vtxStride * numVertices, vtxBuf, GL_STATIC_DRAW);// Using EBO (Element Buffer Object) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLushort) * numIndices, indices, GL_STATIC_DRAW);}//bind VBO every time (VBO including vertex array & color array)glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);glEnableVertexAttribArray(VERTEX_POS_INDX);glEnableVertexAttribArray(VERTEX_COLOR_INDX);// notice offset value(not using buffer pointer)glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,GL_FLOAT, GL_FALSE, vtxStride, (const void *)offset);offset += VERTEX_POS_SIZE * sizeof(GLfloat);glVertexAttribPointer(VERTEX_COLOR_INDX,VERTEX_COLOR_SIZE,GL_FLOAT, GL_FALSE, vtxStride,(const void *)offset);glDrawArrays(GL_TRIANGLES, 0, 3);//glDrawElements ( GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT,0 );glDisableVertexAttribArray(VERTEX_POS_INDX);glDisableVertexAttribArray(VERTEX_COLOR_INDX);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

注意的是此时 glVertexAttribPointer 最后一个参数offset 设置的是 0VERTEX_POS_SIZE

要特别注意的是 使用VBO绘制时,glDrawElements 最后一个参数 indices: 指向一段存储区,缓存在顶点数组中顶点的偏移设置为0

VAO

VAOVertex Array Object)是指顶点数组对象,主要用于管理 VAO或者 EBO,减少 glBindBufferglEnableVertexAttribArrayglVertexAttribPointer 这些调用操作,高效实现在顶点数组之间切换

VAO 和 VBO之间关系.png

  • 正常情况下每次绘制使用 VBO EBO 的操作如下
//第一次需要创建VBO,EBO index,然后拷贝相应数据
......
//excute every time 
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] );glEnableVertexAttribArray ( VERTEX_POS_INDX );
glEnableVertexAttribArray ( VERTEX_COLOR_INDX );glVertexAttribPointer ( VERTEX_POS_INDX, VERTEX_POS_SIZE,GL_FLOAT, GL_FALSE, VERTEX_STRIDE, ( const void * ) 0 );glVertexAttribPointer ( VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE,GL_FLOAT, GL_FALSE, VERTEX_STRIDE,( const void * ) ( VERTEX_POS_SIZE * sizeof ( GLfloat ) ) );glDisableVertexAttribArray(VERTEX_POS_INDX);glDisableVertexAttribArray(VERTEX_COLOR_INDX);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  • 现在使用 VAO,只需要第一次 bind 上面的操作,以后每次 enbale VAO 就可以

示例代码:

#define VERTEX_POS_SIZE       3 // x, y and z
#define VERTEX_COLOR_SIZE     4 // r, g, b, and a#define VERTEX_POS_INDX       0
#define VERTEX_COLOR_INDX     1#define VERTEX_STRIDE         ( sizeof(GLfloat) *     \( VERTEX_POS_SIZE +    \VERTEX_COLOR_SIZE ) )
int Init ( ESContext *esContext )
{UserData *userData = esContext->userData;const char vShaderStr[] ="#version 300 es                            \n""layout(location = 0) in vec4 a_position;   \n""layout(location = 1) in vec4 a_color;      \n""out vec4 v_color;                          \n""void main()                                \n""{                                          \n""    v_color = a_color;                     \n""    gl_Position = a_position;              \n""}";const char fShaderStr[] ="#version 300 es            \n""precision mediump float;   \n""in vec4 v_color;           \n""out vec4 o_fragColor;      \n""void main()                \n""{                          \n""    o_fragColor = v_color; \n""}" ;GLuint programObject;// 3 vertices, with (x,y,z) ,(r, g, b, a) per-vertexGLfloat vertices[3 * ( VERTEX_POS_SIZE + VERTEX_COLOR_SIZE )] ={0.0f,  0.5f, 0.0f,        // v01.0f,  0.0f, 0.0f, 1.0f,  // c0-0.5f, -0.5f, 0.0f,        // v10.0f,  1.0f, 0.0f, 1.0f,  // c10.5f, -0.5f, 0.0f,        // v20.0f,  0.0f, 1.0f, 1.0f,  // c2};// Index buffer dataGLushort indices[3] = { 0, 1, 2 };// Create the program objectprogramObject = esLoadProgram ( vShaderStr, fShaderStr );if ( programObject == 0 ){return GL_FALSE;}// Store the program objectuserData->programObject = programObject;// Generate VBO Ids and load the VBOs with dataglGenBuffers ( 2, userData->vboIds );glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );glBufferData ( GL_ARRAY_BUFFER, sizeof ( vertices ),vertices, GL_STATIC_DRAW );glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] );glBufferData ( GL_ELEMENT_ARRAY_BUFFER, sizeof ( indices ),indices, GL_STATIC_DRAW );// Generate VAO IdglGenVertexArrays ( 1, &userData->vaoId );// Bind the VAO and then setup the vertex // attributes 将下面VBO的操作绑定glBindVertexArray ( userData->vaoId );glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1] );glEnableVertexAttribArray ( VERTEX_POS_INDX );glEnableVertexAttribArray ( VERTEX_COLOR_INDX );glVertexAttribPointer ( VERTEX_POS_INDX, VERTEX_POS_SIZE,GL_FLOAT, GL_FALSE, VERTEX_STRIDE, ( const void * ) 0 );glVertexAttribPointer ( VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE,GL_FLOAT, GL_FALSE, VERTEX_STRIDE,( const void * ) ( VERTEX_POS_SIZE * sizeof ( GLfloat ) ) );// Reset to the default VAOglBindVertexArray ( 0 );glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );return GL_TRUE;
}void Draw ( ESContext *esContext )
{UserData *userData = esContext->userData;glViewport ( 0, 0, esContext->width, esContext->height );glClear ( GL_COLOR_BUFFER_BIT );glUseProgram ( userData->programObject );// Bind the VAO 因为上面已经绑定了VAO,现在只需要这一步操作就可以了glBindVertexArray ( userData->vaoId );// Draw with the VAO settingsglDrawElements ( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, ( const void * ) 0 );// Return to the default VAOglBindVertexArray ( 0 );
}

相关文章:

  • 动态规划引入
  • 【Dify系列教程重置精品版】第五章:Dify配置Ollama
  • C# System.Text.Json终极指南(十):从基础到高性能序列化实战
  • MCP 多工具协作链路设计:打造真正的智能工作流
  • 补题:K - Magic Tree (Gym - 105231K)
  • SpringBoot研究生双选系统开发实现
  • Rust中避免过度使用锁导致性能问题的策略
  • C# | 基于C#实现的BDS NMEA-0183数据解析上位机
  • 详解TypeScript中的类型断言及其绕过类型检查机制
  • Python 从入门到精通3 控制结构
  • 深度学习基础--目标检测入门简介
  • 软件工程国考
  • 使用Python和Pandas实现的Azure Synapse Dedicated SQL pool权限检查与SQL生成用于IT审计
  • PyTorch 与 TensorFlow:深度学习框架的深度剖析与实战对比
  • 等保系列(一):网络安全等级保护介绍
  • 第四章 Maven
  • 世纪华通:从财报数据看其在游戏领域的成功与未来
  • 3D版同步帧游戏
  • PyTorch中“原地”赋值的思考
  • GPU虚拟化实现(六)
  • 5月资金面前瞻:政府债净融资规模预计显著抬升,央行有望提供流动性支持
  • 保险经纪公司元保在纳斯达克挂牌上市,去年净赚4.36亿元
  • 屠呦呦当选美国国家科学院外籍院士
  • 徐徕任上海浦东新区副区长,此前已任区委常委
  • 校长套取学生伙食费设小金库,重庆通报6起违反八项规定典型问题
  • 热点问答|第三轮间接谈判结束,美伊分歧还有多大?