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

news2024/11/28 8:39:28

目录

      • 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 indices
    if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
        // Only allocate on the first draw
        glGenBuffers(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 indices
    if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
        // Only allocate on the first draw
        glGenBuffers(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 indices
    if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0) {
        // Only allocate on the first draw
        glGenBuffers(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 id
        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);
}

主要步骤是:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1937935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LoadRunner-Vugen脚本使用教程

1 使用VuGen录制脚本 1.1新建脚本和解决方案 (1)打开VuGen,单击【File】→【New Script and Solution】创建项目,弹出Create a New Script对话框。 左侧栏是协议分类,每项含义如下所示: ● Single Pro…

数字通云平台 智慧政务OA PayslipUser SQL注入漏洞复现

0x01 产品简介 数字通云平台智慧政务OA产品是基于云计算、大数据、人工智能等先进技术,为政府部门量身定制的智能化办公系统。该系统旨在提高政府部门的办公效率、协同能力和信息资源共享水平,推动电子政务向更高层次发展。 0x02 漏洞概述 数字通云平台 智慧政务OA Paysli…

pytorch学习(六)transforms使用

1.Transforms可以对训练图像进行预处理,以提高模型的稳定性,提高泛化能力。其中包含: 中心裁剪、数据标准化、缩放、裁剪、旋转、仿射、反转、填充、噪声添加、灰度变换、线性变换、亮度饱和度以及对比度变换等。 所处理的图像用tensorboard…

【iOS】APP仿写——网易云音乐

网易云音乐 启动页发现定时器控制轮播图UIButtonConfiguration 发现换头像 我的总结 启动页 这里我的启动页是使用Xcode自带的启动功能,将图片放置在LaunchScreen中即可。这里也可以通过定时器控制,来实现启动的效果 效果图: 这里放一篇大…

基于视觉工具箱和背景差法的行人检测,行走轨迹跟踪,人员行走习惯统计matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 在三维图中,幅度越大,则表示人员更习惯的行走路线。 2.算法运行软件版本 matlab2022a 3.部分核…

【Nacos】Nacos服务注册与发现 心跳检测机制源码解析

在前两篇文章,介绍了springboot的自动配置原理,而nacos的服务注册就依赖自动配置原理。 Nacos Nacos核心功能点 服务注册 :Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端…

【C语言】深入解析希尔排序

文章目录 什么是希尔排序?希尔排序的基本实现代码解释希尔排序的优化希尔排序的性能分析希尔排序的实际应用结论 在C语言编程中,希尔排序是一种高效的排序算法,是插入排序的一种更高效的改进版本。它通过比较相距一定间隔的元素来进行排序&am…

JRT报告打印设计

检验报告单打印一直是个难点问题,JRT开发时候重点考虑了简化检验报告打印,首先采用脚本化方便快速修改报告。然后打印基础解决难点问题,基于JRT打印就可以简化到本文代码的水平,维护方便,结构清晰,上线修改…

【1】Spring Cloud 工程搭建

🎥 个人主页:Dikz12🔥个人专栏:Spring学习之路📕格言:吾愚多不敏,而愿加学欢迎大家👍点赞✍评论⭐收藏 目录 1. 父子工程创建 1.1 创建父工程 1.2 创建子项目 重点关注Spring C…

头发稀疏治疗笔记

1. 前言 今天去中南医院看了一下“头发稀疏”的病症; 2. 头皮检测 2.1 毛发光镜检查 2.2 皮肤镜影像

root的安卓12系统上,如何使apk获得root或者高级别的系统权限?

🏆本文收录于《CSDN问答解答》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&…

JVM--经典的垃圾收集器

1. 垃圾收集器的概念 定义 ; 如果说收集算法是内存回收的方法论,那垃圾收集器就是内存回收的实践者。《Java虚拟机规 范》中对垃圾收集器应该如何实现并没有做出任何规定,因此不同的厂商、不同版本的虚拟机所包含 的垃圾收集器都可能会有很大差别…

Linux journalctl日志太长,如何倒序查看journalctl --reverse,journalctl -xeu

文章目录 需求实验方法一方法二 需求 Linux journalctl日志太长,如何倒序查看 我们通常关心的是最近的日志,但是每次打开日志都是按时间先后顺序显示的,如何倒序查看,请看下面: 实验 方法一 journalctl 命令默认按…

uniapp封装请求拦截器,封装请求拦截和响应拦截的方法

首先我们先看一下uni官方给开发者提供的uni.request用来网络请求的api 1 2 3 4 5 6 7 8 9 uni.request({ url: , method: GET, data: {}, header: {}, success: res > {}, fail: () > {}, complete: () > {} }); 可以看到我们每次请求数据的时候都需…

两种调用方法可以让Contact form 7表单在任意地方显示

Contact form 7是wordpress建站过程中最常用到的插件之一,不过,在Contact form 7调用的时候,有些新手还是搞不太清楚它的调用方法。下面简站wordpress小编,就把常用的两种调用方法,分享给大家: Contact fo…

Open3D 非线性最小二乘法拟合空间球

目录 一、概述 1.1原理 1.2实现步骤 二、代码实现 2.1关键代码 2.1.1定义残差函数 2.1.2拟合球面 2.2完整代码 三、实现效果 3.1原始点云 3.2拟合后点云 3.3结果数据 前期试读,后续会将博客加入下列链接的专栏,欢迎订阅 Open3D点云算法与点…

题解|2023暑期杭电多校02

【原文链接】 (补发)题解|2023暑期杭电多校02 1002.Binary Number 字符串、贪心 题目大意 给定一段长度为 n n n的01串,首位保证为1 任选定其中任意长的一段并将其反转 必须执行以上操作 k k k次,求操作后得到的01串表示的二进…

JAVA.2.运算符

目录 1.算术运算符 小数有可能会不精确,整数除以整数得整数 例子 package demo1;public class Hello {public static void main(String[] args) {System.out.println(12);System.out.println(1-2);System.out.println(5/2);System.out.println(5.0/2);System.out.…

代码随想录移除元素二刷

代码随想录移除元素二刷 leetcode 27 这道题思路的话可以这样去理解,用两个指针,一个慢指针,一个快指针。先让快指针往前面去探路,也就是去遍历数组,遇到不为val的值再去把该值赋值给nums[slow],slow指针1…

3.1、matlab双目相机标定实验

1、双目相机标定原理及流程 双目相机标定是将双目相机系统的内外参数计算出来,从而实现双目视觉中的立体测量和深度感知。标定的目的是确定各个摄像头的内部参数(如焦距、主点、畸变等)和外部参数(如相机位置、朝向等&#xff09…