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

OpenGL-ES 学习(7) ---- VBO EBO 和 VAO

目录

      • VBO(Vertex Buffer Object)
      • EBO(Element Buffer Object)
      • VAO(Vertex Array Object)

VBO(Vertex Buffer Object)

EBO(Element Buffer Object)

VBO(Vertex Buffer Object) 实际是指顶点缓冲器对象

opengl-es 2.0 的编程中,用于绘制图元的顶点数据是从 CPU 传递到显存中的,具体的方式通过 glVertexAttribPointer 设定顶点或者顶点颜色的值到具体的 index中,OES 在shader 程序中根据 index 获取对应的值,编程方法如下面的示例所示:

static GLfloat vertices[] = {0.0f,  0.5f, 0.0f,-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f
};static GLfloat verticesColor[] = {1.0f,  0.0f, 0.0f,0.0f,  1.0f, 0.0f,0.0f,  0.0f, 1.0f,
};....
{
// 指定对应的 vertices 到 index 0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, verticesColor);
glEnableVertexAttribArray(1);// Draw the triangle
glDrawArrays(GL_TRIANGLES, 0, 3);
}

但是如果每次都要绘制相同的图元,就可以把 顶点的值缓存到GPU内存中,不必每次都执行设定加拷贝的过程,这里就可以使用 VBO

不仅顶点的坐标可以缓存到GPU显存中,不同的顶点构成的基本图元索引也可以进行缓存,这就是 EBO(Element buffer Obejct)

VAO 和 EBO 的作用是在显存中提前开辟好一块内存,用于缓存顶点数据或者图元索引数据,从而避免绘制时的 CPU 和 GPU之间的内存拷贝,可以改进渲染性能,降低内存带宽和功耗

GL_ARRAY_BUFFER 标志指定的缓冲区对象用于保存顶点数组
GL_ELEMENT_ARRAY_BUFFER 标志指定的缓存区对象用于保存图元索引

单独使用 VBO 缓存进行绘制的代码如下(实际也使用了VAO):

static void DrawPrimitiveWithVBOs(ESContext *esContext)
{UserData *userData = esContext->userData;GLuint   offset = 0;// vboIds[0] - used to store vertex attribute data// vboIds[l] - used to store element indicesif (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {// Only allocate on the first drawglGenBuffers(2, userData->vboIds);glGenVertexArrays(1, &userData->vaoId);glBindVertexArray(userData->vaoId);// 用于缓存顶点的数据glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3,GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat) , NULL);glEnableVertexAttribArray(0);// notice using GL_ARRAY_BUFFER we not using EBO there// 用于缓存顶点颜色数据 // 顶点和顶点颜色都是使用 VBO 进行缓存glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[1]);glBufferData(GL_ARRAY_BUFFER,sizeof(verticesColor), verticesColor, GL_STATIC_DRAW);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat) ,NULL);glEnableVertexAttribArray(1);glBindVertexArray(0);}glBindVertexArray(userData->vaoId);glDrawArrays(GL_TRIANGLES, 0, 3);glBindVertexArray(0);
}

注意上面使用了 VBO 同时缓存了顶点和顶点颜色数据,但是没有使用 EBO,但是也使用了VAO,根据chatgpt的回答,无法只使用 VBO 不结合 VAO,但是可以不是 VAO
注意 VBO 的使用方法需要先 glBindBuffer 最后 glEnableVertexAttribArray

同时使用 VBOEBO 进行绘制的代码如下:

unsigned int indices[] = {// 前面0, 1, 2,2, 3, 0,// 后面4, 5, 6,6, 7, 4,// 左面8, 9, 10,10, 11, 8,// 右面12, 13, 14,14, 15, 12,// 顶面16, 17, 18,18, 19, 16,// 底面20, 21, 22,22, 23, 20
};static void DrawPrimitiveWithVBOs(ESContext *esContext)
{UserData *userData = esContext->userData;GLuint   offset = 0;// vboIds[0] - used to store vertex attribute data// vboIds[l] - used to store element indicesif (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {// Only allocate on the first drawglGenBuffers(2, userData->vboIds);glGenVertexArrays(1, &userData->vaoId);printf("gen vbo id:%d %d vao id:%d.\n",userData->vboIds[0],userData->vboIds[1],userData->vaoId);glBindVertexArray(userData->vaoId);// 使用 VBO 缓存 顶点和顶点颜色数据,注意这里只使用了一个 VBO, 使用 stride 和 offset 进行区分glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3,GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) , NULL);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) ,(void*)(3*sizeof(GLfloat)));glEnableVertexAttribArray(1);// notice using GL_ELEMENT_ARRAY_BUFFER// 使用 VBO 缓存顶点的索引数据glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices), indices, GL_STATIC_DRAW);glBindVertexArray(0);}glBindVertexArray(userData->vaoId);glUniformMatrix4fv(userData->mvpLoc, 1, GL_FALSE, (GLfloat *)&userData->mvpMatrix.m[0][0]);glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_INT,0);glBindVertexArray(0);
}

上面的代码中同时使用了VBOEBOVAO进行绘制,注意这里有一个技巧:一个VBOID可以缓存多个数据,可以通过Strideoffset 的方法进行区分
stride_vbo
对于每个顶点来说,顶点坐标位于前面,offset0 sizeof(GLFloat)),长度为 3 sizeof(GLFloat)),纹理坐标位于后面, offset3 sizeof(GLFloat)), 长度为 2 sizeof(GLFloat)),每个顶点的步长(stride)就是3+2(sizeof(GLFloat))

VAO(Vertex Array Object)

VAO 是指顶点数组对象,主要用于管理VBO或者EBO,减少 glBindBufferglEnableVertexAttribArrayglVertexAttribPointer 这些调用操作,高效的在顶点属性配置之间切换
VAO
使用VAO绘制的示例如下:

static void DrawPrimitiveWithVBOs(ESContext *esContext)
{UserData *userData = esContext->userData;GLuint   offset = 0;// vboIds[0] - used to store vertex attribute data// vboIds[l] - used to store element indicesif (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {// Only allocate on the first drawglGenBuffers(2, userData->vboIds);glGenVertexArrays(1, &userData->vaoId);printf("gen vbo id:%d %d vao id:%d.\n",userData->vboIds[0],userData->vboIds[1],userData->vaoId);// 开始绑定 vao idglBindVertexArray(userData->vaoId);// 使用 VBO 缓存 顶点和顶点颜色数据,注意这里只使用了一个 VBO, 使用 stride 和 offset 进行区分glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3,GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) , NULL);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat) ,(void*)(3*sizeof(GLfloat)));glEnableVertexAttribArray(1);// notice using GL_ELEMENT_ARRAY_BUFFER// 使用 VBO 缓存顶点的索引数据glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices), indices, GL_STATIC_DRAW);// 结束绑定glBindVertexArray(0);}// 开始使用glBindVertexArray(userData->vaoId);glUniformMatrix4fv(userData->mvpLoc, 1, GL_FALSE, (GLfloat *)&userData->mvpMatrix.m[0][0]);glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_INT,0);glBindVertexArray(0);
}

主要步骤是:

  • glGenVertexArrays 生产 vao id
  • glBindVertexArray(userData->vaoId); 开始绑定 vao 的操作
  • glBindVertexArray(0); 结束绑定 vao 的操作

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【人工智能】AI音乐创作兴起与AI伦理的新视角
  • 矩阵分析——线性积分方程组的矩阵解法研究
  • 华为OD机试(C卷,200分)- 二叉树计算
  • MYSQL2
  • Rudolph and Mimic
  • linux LED代码设计
  • 图形渲染基础-Unity渲染管线介绍
  • 基于Python+Django,开发的一个在线教育系统
  • Docker Machine 深入解析
  • qt log 输出为文件,每分钟换一个log文件
  • 2024杭电多校1——1008位运算
  • mysql的索引、事务和存储引擎
  • 大模型实战—大模型赋能网络爬虫
  • 配置文件格式 JSON 快速上手
  • 分布式 I/O 系统 BL200 Modbus TCP 耦合器
  • [Vue CLI 3] 配置解析之 css.extract
  • CentOS 7 防火墙操作
  • CSS实用技巧
  • httpie使用详解
  • Intervention/image 图片处理扩展包的安装和使用
  • Java编程基础24——递归练习
  • js学习笔记
  • Laravel 实践之路: 数据库迁移与数据填充
  • mysql中InnoDB引擎中页的概念
  • PermissionScope Swift4 兼容问题
  • uni-app项目数字滚动
  • 半理解系列--Promise的进化史
  • 动态魔术使用DBMS_SQL
  • 解析 Webpack中import、require、按需加载的执行过程
  • 今年的LC3大会没了?
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 前端攻城师
  • 使用Gradle第一次构建Java程序
  • 使用SAX解析XML
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 我有几个粽子,和一个故事
  • 线上 python http server profile 实践
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​比特币大跌的 2 个原因
  • ​补​充​经​纬​恒​润​一​面​
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (k8s)Kubernetes 从0到1容器编排之旅
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)平衡树
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .stream().map与.stream().flatMap的使用
  • /*在DataTable中更新、删除数据*/
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • @Bean注解详解
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • @德人合科技——天锐绿盾 | 图纸加密软件有哪些功能呢?
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
  • [AIGC] SQL中的数据添加和操作:数据类型介绍