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

【Opengl概念】VBO和VAO到底是个啥关系?

目录

  • 一、前言
  • 二、VAO和VBO的关系
    • 2.1 什么是VAO、VBO
    • 2.2 如何将多个VBO投入到同一个VAO名下
  • 三、使用VBO注意点
    • 3.1 什么是VBO
    • 3.2 如何创建VBO
    • 3.3 VBO可能绑定的目标
    • 3.4 给VBO灌入数据
    • 3.5 给VBO数据定义指针
  • 四、关于VAO下操作事项

一、前言

   对于Opengl中的VBO和VAO相信很多人都熟悉这2个名字,但是有时候缺容易混淆2个概念或者说不理解这2个概念的作用是什么。本文对这2个概念做了对比和参照。

二、VAO和VBO的关系

2.1 什么是VAO、VBO

   对于VBO我们首先应该立刻明白,它就是命令GPU生成一个Buffer,并且通过VBO这个标识符号访问操作。那么VAO呢?VAO是个虚的标识符号,在传唤VAO时候,VAO旗下的所有VBO都被执行。
   这里打个浅显的比方:VAO表示一个文件夹,VBO是这个文件夹下的具体文件。当我们用:

COPY VAO sss; 

   将VAO和其中内容全部拷贝到sss下面,因此,VAO下面的VBO自然也不例外了。手册上说VAO是管理VBO的,这样说有点过头,其实VOA从来没有管理过VBO,只是简单标注一下而已。

2.2 如何将多个VBO投入到同一个VAO名下

   这个问题很简单,我们将用一个实际小例子回答:
step1:创建一个VAO

unsigned int VAO;
glGenVertexArrays(1, &VAO);

   此步骤告诉GPU。已经有一个VAO试图对应某些缓存。

step2:打开一个VAO

glBindVertexArray(VAO);

   此处打开一个VAO,好比操作系统打开一个空文件夹。有待于建立文件。下面操作均在VOA名下完成。
step3:在一个VAO下挂载多个VBO
   1)建立一个VBO1

unsigned int VBO1;
glGenBuffers(1, &VBO1);
glBindBuffer(GL_ARRAY_BUFFER, VBO1);   此处打开一个VBO1,后面操作均在VBO1之下
... some operation... 

   以上对VBO1进行操作,操作后关闭此VBO1:

glBindBuffer(GL_ARRAY_BUFFER, 0); 解绑上文的VBO1

   2)再建立一个VBO2

unsigned int VBO2;
glGenBuffers(1, &VBO2);
glBindBuffer(GL_ARRAY_BUFFER, VBO2);   此处打开一个VBO2,后面操作均在VBO1之下
... some operation... 

基本上按照上面操作,这里不再赘述;值得强调的是,这里统统在VAO打开的前提操作,因此自然全挂在VAO名分之下。
   3)VAO解绑

glBindVertexArray(0);

此处关闭VAO状态,以后的VBO建立自然不在VAO名下。
step4:以后使用VAO
很简单,只要:

glBindVertexArray(VAO);
下面可以使用VBO1和VBO2了

三、使用VBO注意点

3.1 什么是VBO

   VBO全名顶点缓冲对象(Vertex Buffer Object),他主要的作用就是可以一次性的发送一大批顶点数据到显卡上,而不是每个顶点发送一次。我们知道CPU传送数据给GPU其实是比较耗费时间的,所以尽可能的一次性把需要的顶点数据全部传给GPU,这样顶点着色器几乎能立即访问到顶点,有助于加快顶点着色器效率。

3.2 如何创建VBO

   就目前所有游戏引擎来说VBO的机制已经是基础了,我们看下VBO在Opengl中是如何创建的。
   首先,我们需要在Opengl生成一个缓冲类型的ID。

unsigned int VBO;
glGenBuffers(1, &VBO);

   这里我们创建了一个缓冲的ID,当然了你也可以通过数组来批量创建一系列的VBO的ID,

unsigned int VBO[3];
glGenBuffers(3,VBO);

   注意:这个时候GPU并不产生缓存。相当于定义了一个空指针。
   接下来,我们要将这个ID绑定给指定类型来告诉Opengl这个缓冲是什么类型的,此操作相当于对buffer的数据属性进行指定;

glBindBuffer(GL_ARRAY_BUFFER, VBO);

   这里我们将VBO赋予了GL_ARRAY_BUFFER的类型,告诉Opengl这个VBO变量是一个顶点缓冲对象。
   注意1,这里是顶点类型的缓存,也可能是其它的缓存。
   注意2,从这一刻起,我们使用的任何(在GL_ARRAY_BUFFER目标上的)缓冲操作都会用来配置当前绑定的缓冲(VBO)

至此对于VBO的声明就结束了,其实也是蛮简单的,我们回顾下:
1)生成一个缓冲类型的ID;
2)指定ID的缓冲类型为GL_ARRAY_BUFFER;

3.3 VBO可能绑定的目标

   上文谈到 *glBindBuffer(GL_ARRAY_BUFFER, VBO);*绑定了VBO
到GL_ARRAY_BUFFER类型缓存。其实可以绑定的缓存有很多种,下表列出:
在这里插入图片描述

3.4 给VBO灌入数据

   接下来我们看下,如何给VBO赋值,我们假定有如下三角形的顶点数据,此数据在CPU一侧,待灌入VOB:

float vertices[] = { 	                             // 位置              // 颜色0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部};

   Opengl通过glBufferData接口来复制顶点数据到缓冲中供Opengl使用,对于上面的三角形顶点数据,代码就可能是这样的:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

   第一个参数表示目标的缓冲类型,这里指当前绑定到GL_ARRAY_BUFFER上的顶点缓冲对象,第二个参数表示数据大小(字节为单位),第三个参数表示我们实际发出的数据。第四个参数GL_STATIC_DRAW表示Opengl如何处理上传的数据,Opengl中一共有三种类型:

GL_STATIC_DRAW :数据不会或几乎不会改变。
GL_DYNAMIC_DRAW:数据会被改变很多。
GL_STREAM_DRAW :数据每次绘制时都会改变。

   到这一步,我们的数据就已经上传到GPU上去了。一个VBO过程也就结束了。

3.5 给VBO数据定义指针

   那这里引申出一个问题,我们看到vertices中有注释,表明这个数组里面有两种数据,一种是顶点坐标,一种是顶点颜色,那都放在一个float的数组中,Opengl如何来区分他们呢?这里就要用到glVertexAttribPointer这个接口了,该接口就是帮助Opengl解释如何处理数据的。我们来看下对于vertices的数据处理的代码:

	// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

   我们分析下这段代码,首先我们来解释下glVertexAttribPointer的各个参数的意义。

   第一个参数表示我们希望数据中哪部分数据放在对应的Location位置上,这个可能主要体现在shader部分,我们看下顶点着色器的代码:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

   我们根据变量名大致能猜出来location为0的是顶点坐标,location为1的是颜色值,这里的location就是对应了上面代码种glVertexAttribPointer第一个参数,location没有准确的位置定义,并没有说location为0的一定要是顶点坐标属性,也可以是颜色或者uv坐标属性,只是常规来说,我们习惯将坐标放在第一个位置。

   glVertexAttribPointer第二个参数表示属性大小,坐标和颜色的大小都是3,所以这里填3;
   第三个参数表示数据类型;
   第四个参数表示我们是否希望数据标准化。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE;
   第五个参数表示步长,数据之间的间隔,我们这里不管坐标还是颜色都是3个分量,所以坐标和坐标数据之间隔了6个float字节,颜色和颜色之间隔了6个float字节;
   第六个参数表示偏移量,即在一段数据中,指定的数据偏移多少位置开始。在这里,坐标数据都是每段数据的起始位置,所以偏移量是0,而颜色数据在坐标数据之后,坐标数据有3个分量,所以每个颜色数据偏移三个float字节开始算;
   每次设定好一个location的值之后,记得要开启对应的位置数据glEnableVertexAttribArray,因为Opengl默认是全关闭的。
   经过glVertexAttribPointer执行之后,shader中对应layout的位置数据就有对应的值了。

四、关于VAO下操作事项

1. 顶点数组对象(Vertex Array Object, VAO)可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。 2. 一个顶点有多种属性:位置,一种或多种颜色,一个或多个纹理坐标等。 3. 这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑 定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中。 4. 要想使用VAO,要做的只是使用glBindVertexArray绑定VAO。 5. 从绑定之后起,我们应该绑定和配置对应的VBO和属性指针,之后解绑VAO供之后使用。当我们打算绘制一个物体的时候,我们只要在绘制物体前简单地把VAO绑定到希望使用的设定上就行了。(这里很重要,也就是说在渲染循环里面只需要绑定VAO就行了不需要绑定VBO)。 6. 但是在渲染循环中,如果我们只有一个VAO,也不必每次都绑定。

相关文章:

  • 量化算子的介绍
  • SpringMVC | SpringMVC中的 “数据绑定”
  • 【C#】.net core 6.0 使用第三方日志插件Log4net,日志输出到控制台或者文本文档
  • 深度学习基础知识之Atrous卷积(空洞卷积)
  • Vue 3中的reactive:响应式状态的全面管理
  • 软件工程师,是时候了解下Rust编程语言了
  • 机器学习常用框架
  • 蓝桥杯--冶炼金属
  • Centos7部署使用TELEMAC-MASCARET
  • IO进线程练习(用到了:文件IO 标准IO 多进程 exec进程转移 有名管道 无名管道)
  • 我们是否生活在一个超大型生物的大脑之中?——对多元宇宙观与生命存在形式的哲学探讨
  • C++常用容器总结
  • SwiftUI的组件-Slider
  • 关于原型的一些总结
  • JVM的工作流程
  • JavaScript-如何实现克隆(clone)函数
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 78. Subsets
  • CODING 缺陷管理功能正式开始公测
  • ERLANG 网工修炼笔记 ---- UDP
  • JavaScript中的对象个人分享
  • Linux链接文件
  • Python_网络编程
  • quasar-framework cnodejs社区
  • Rancher如何对接Ceph-RBD块存储
  • SOFAMosn配置模型
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • Unix命令
  • 得到一个数组中任意X个元素的所有组合 即C(n,m)
  • 第2章 网络文档
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 机器学习学习笔记一
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 码农张的Bug人生 - 见面之礼
  • 前端自动化解决方案
  • 算法之不定期更新(一)(2018-04-12)
  • 探索 JS 中的模块化
  • 线性表及其算法(java实现)
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 想使用 MongoDB ,你应该了解这8个方面!
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • # 透过事物看本质的能力怎么培养?
  • (11)MSP430F5529 定时器B
  • (33)STM32——485实验笔记
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (70min)字节暑假实习二面(已挂)
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (笔试题)分解质因式
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (三分钟)速览传统边缘检测算子