当前位置:首页 > 其他 > 正文内容

LearnOpenGL 笔记 -- VAO & VBO

邻居的猫1个月前 (12-09)其他1330

1 前语

VAO和VBO归于咱们学习opengl最早触摸的几个概念,最开端学习的时分有或许无法直观的了解这个概念的效果和运用办法。笔者也是opengl新手,在此记载学习的相关笔记,便于之后进行检查。本文首要参阅learnopengl 教程以及 opengl官网 中的用法和解说,文中的代码实例运用opengl3.3,过早版别或许无法正常运转。

2 Vertex Specification

在解说VAO和VBO之前,咱们需求了解其所属的烘托流水线阶段,在opengl wiki中,这一阶段被称为Vertex Specification,在接下来的文章中,咱们将其称为极点标准,以下为官网的原文解说

Vertex Specification is the process of setting up the necessary objects for rendering with a particular shader program, as well as the process of using those objects to render.
极点标准是指为特定着色器设置其所需求的方针,以及运用这些方针进行烘托的进程

仅从这个解说咱们无法很好的了解这一进程究竟做些什么,但咱们能够针对这个解说提出几个问题:

  1. 特定着色器是指哪些着色器
  2. 所需求的方针是指什么
  3. 这些方针关于着色器有什么效果

接下来所介绍的概念将结尾答复以上的这些问题,在了解了这几个问题之后,咱们也能够十分天然的了解VBO和VAO的概念以及效果

2.1 Vertex Stream

为了进行烘托,咱们需求运用着色器以及调用这些着色器的烘托管线,在opengl中首要包含极点着色器(Vertex Shader),几许着色器(Geometry Shader), 片元着色器(Fragment Shader)。其间,极点着色器包含用户界说的输入变量列表,这些变量代表每个极点在烘托时所需求的特点

一个常见的极点着色器输入变量列表

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;

这个变量列表表明的是location0输入极点的坐标特点,location1输入法向量特点,location2输入纹路坐标特点。在界说了这些输入特点之后,咱们需求为其“预备”好相应的数据。

关于每个特点,咱们需求供给一个数据数组,并且这些数据数组有必要有相同的元素个数 (这儿的数组相似c言语的数组概念,可是愈加灵敏),咱们把这些数据数组称为极点数据。

需求留意的是,这些极点的次序是十分重要的,这个次序决议了OpenGL会怎么烘托这些极点组成的Primitive(图元),OpenGL将这种按次序进行定点数据传输的进程称为Vertex Stream(极点流)。

OpenGL从极点数组中发生极点流的办法有两种,一种是直接依照数组的原始次序获取极点数据,另一种则是界说一个索引列表。索引列表界说了获取极点数据的先后次序,并且索引列表能够屡次拜访同一数据元素。

例如,咱们的极点坐标数据数组如下:

{ {1, 1, 1}, {0, 0, 0}, {0, 0, 1} }

假如咱们选用第一种极点流生成办法,那么OpenGL会直接依照次序从左到右处理这些数据,但当咱们运用第二种索引列表的办法时,则能够自界说关于这些数据的处理次序和次数

例如,这样的索引列表

{2, 1, 0, 2, 1, 2}

那么,依照该索引列表中的次序去对极点数据数组进行拜访,咱们能够取得这样的极点流

{ {0, 0, 1}, {0, 0, 0}, {1, 1, 1}, {0, 0, 1}, {0, 0, 0}, {0, 0, 1} }

上述比方来历于Opengl wiki ,这种运用索引的极点流是一种很好的数据压缩手法,关于重复运用的极点来说,其极点数据一般会占用32字节左右的内存,而一个索引则一般只需2-4字节。

留意 : 咱们或许会想,能不能经过为每个特点赋予独立的索引列表,最大化这种数据压缩的才能?一些3d修正东西支撑这种操作,可是OpenGL(同Direct3D)是不允许的,其要求一切极点数据数组同享同一个索引列表,因而从其他3d鸿沟东西中导入的极点数据中假如含有多个索引列表,则需求进行相应的预处理使其能够同享同一个索引列表

2.2 Primitive(图元)

个人认为图元的概念最直接的运用是在几许着色器中,因而这儿只会进行一个大约的介绍。

上述的极点流输出的究竟只是极点,正如咱们平常手艺绘画相同,并非逐点制作,而是组合各种线段和几许图形,关于OpenGL而言,这些根本的绘画元素被称为图元。咱们需求告知OpenGL怎么处理极点流输出的极点数据然后组成各式各样的图元。例如将每三个极点组成一个三角形,每两个极点组成一个线段,亦或许将4个极点组成两个三角形

2.3 总结

经过上述几个概念的介绍,咱们应该能够答复之前在Vertex Specification中提出的几个问题

Q1 : 特定着色器是指哪些着色器
A1 : 咱们在极点着色器中界说其所需求的极点特点,并为每个特点预备其对应的数据数组。并且经过这些数据数组发生的极点流,来让OpenGL组成图元。所以这儿的着色器首要是指极点着色器,但运用几许着色器时也会触及到这个概念。

Q2 : 所需求的方针是指什么
A2 : 所需求的方针应该至少包含极点特点数据,关于运用索引进行极点流发生的办法还应该预备一个特点之间同享的索引列表。

Q3 : 这些方针关于着色器有什么效果
A3 : 这些方针为极点着色器的极点特点供给数据来历,并且经过极点流供给给OpenGL组成图元的数据处理次序,后半部分或许在GLSL本身的着色器代码中表现的不是很明显,可是关于光栅化阶段来说则是十分重要的。

3 VAO&VBO

从第二节中的概念咱们知道,要进行Vertex Specification, 咱们需求能够供给数据数组并发生极点流的方针, 这也正是VAO和VBO存在的含义。

3.1 VBO(Vertex Buffer Object)

极点着色器在GPU中创立内存用于存储极点数据,这些内存在显卡中,因而也被称为显存(vRam)。咱们经过装备OpenGL来“解说”这些内存,即不同的数据段对应的是极点特点中的哪一部分。而OpenGL办理这些内存的办法即为极点缓冲方针(Vertex Buffer Objects,VBO),咱们能够经过创立与装备这些方针来请求显存空间以及解说这些内存。

以下为LearnOpengl中对运用VBO优点的解说

The advantage of using those buffer objects is that we can send large batches of data all at once to the graphics card, and keep it there if there's enough memory left, without having to send data one vertex at a time. Sending data to the graphics card from the CPU is relatively slow, so wherever we can we try to send as much data as possible at once. Once the data is in the graphics card's memory the vertex shader has almost instant access to the vertices making it extremely fast
咱们经过极点缓冲方针(Vertex Buffer Objects, VBO)办理这个内存,它会在GPU内存(一般被称为显存)中贮存许多极点。运用这些缓冲方针的优点是咱们能够一次性的发送一大批数据到显卡上,而不是每个极点发送一次。从CPU把数据发送到显卡相对较慢,所以只需或许咱们都要测验尽量一次性发送尽或许多的数据。当数据发送至显卡的内存中后,极点着色器简直能当即拜访极点,这是个十分快的进程。

VBO的优点在于其能够将许多数据一次发送到显存中,这关于图形烘托十分重要,究竟CPU和GPU之间的通讯是贵重的,咱们不期望在每帧之间进行许多的这种数据传输操作。可是,当咱们只是想制作一些简略的图形进行测验的时分,OpenGL也供给了发送单个极点数据的托言

在Cherno的OpenGL教程中运用的是,其运用的OpenGL3种的接口是

void glVertex2f(GLfloat x,GLfloat y)

而在OpenGL4的reference page中则是这样的接口

void glVertexAttrib2f(GLuint index,GLfloat v0,GLfloat v1)

opengl官网中现在只能找到gl4的reference page,需求gl3以及更老版其他接口查询能够拜访docs.gl

尽管接口发生了少许改动,可是这一方针的规划逻辑仍然是共通的,所以咱们也经过opengl wiki中关于这一概念的叙说来取得愈加全面的知道

Vertex Buffer Object (VBO) is the common term for a normal Buffer Object when it is used as a source for vertex array data. It is no different from any other buffer object, and a buffer object used for Transform Feedback or asynchronous pixel transfers can be used as source values for vertex arrays.

官网的解说是说,VBO是缓冲方针被用在极点数组数据是的俗称, 其和一般的缓冲方针没有差异。换句话说,OpenGL所支撑的缓冲方针理论上都能够用作极点缓冲方针,比方Transform Feedback和asychronous pixel transfers的缓冲方针也能够作为极点缓冲方针运用,详细的操作触及极点后处理(Vertex post-processing),纹路(Texture)缓冲等内容,在相应的章节再详细解说,这儿先只需求知道VBO并不是一种特其他缓冲方针,其他操作的缓冲方针相同能够作为VBO运用。

在OpenGL中咱们能够创立VBO,绑定方针,以及设定VBO对极点数据的解说办法

// 极点缓冲方针的ID
unsigned int VBO;
// 创立一个极点缓冲方针
glGenBuffers(1, &VBO);
// 将极点缓冲方针绑定到GL_ARRAY_BUFFER,从这一刻起,咱们运用的任何(在GL_ARRAY_BUFFER方针上的)缓冲调用都会用来装备当时绑定的缓冲(VBO)
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 将界说的极点数据仿制到缓冲的内存中,第三个参数为NULL时则不进行仿制,仅请求规则巨细的内存,且内存为为初始化状况
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 删去极点缓冲方针
glDeleteBuffer(1,&VBO);

glBufferData的最终一个参数usage指定来咱们期望显卡怎么办理给定的数据,例如一下三种方法:

  • GL_STATIC_DRAW :数据不会或简直不会改动。
  • GL_DYNAMIC_DRAW:数据会被改动许多。
  • GL_STREAM_DRAW :数据每次制作时都会改动。
    当数据在每次烘托中不改动时最好运用GL_STATIC_DRAW, 而当一个缓冲中的数据被频频改动时则运用GL_DYNAMIC_DRAW或GL_STREAM_DRAW,然后保证显卡将数据放入告知写入的内存部分。当然除这三种外还包含许多其他类型,详细的能够参阅文档,不同的显卡厂商关于这些类型有自己的完结来进步缓冲方针的功能,可是并不影响OpenGL开发者对这些数据的运用。

在gl4中咱们能够运用glBufferData的上位代替,详细见文档

void glBufferStorage(GLenum target,GLsizeiptr size,const GLvoid * data,GLbitfield flags);

void glNamedBufferStorage(GLuint buffer,GLsizeiptr size,const void *data,GLbitfield flags);

VBO能够请求对应的内存区域,一起也使咱们能够解说这些数据
例如以下这些接口

void glVertexAttribPointer( GLuint index​, GLint size​, GLenum type​, GLboolean normalized​, GLsizei stride​, const void *offset​);

void glVertexAttribIPointer( GLuint index​, GLint size​, GLenum type​, GLsizei stride​, const void *offset​);

void glVertexAttribLPointer( GLuint index​, GLint size​, GLenum type​, GLsizei stride​, const void *offset​);
这些接口根本完结一个工作,即设置特点索引为index特点的格局以及贮存信息。该函数告知咱们特点index会从绑定到type的缓冲方针获取数据,并且咱们需求留意这种相关是在这个函数被调用时树立的。

例如下面这个比方

glBindBuffer(GL_ARRAY_BUFFER, buf1);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
buf1首要被绑定到GL_ARRAY_BUFFER方针,然后设定特点0的格局与存储信息,然后将0绑定到GL_ARRAY_BUFFER方针,可是这并不影响特点0和buf1之间的相关,咱们能够将GL_ARRAY_BUFFER看作一个设定buf1与特点0的接口,换绑接口并不会影响之前设定的联络

当0绑定到GL_ARRAY_BUFFER时,无法调用glVertexAttribPointer,因而在完结一个极点缓冲方针的设定后,咱们一般会将将0绑定到GL_ARRAY_BUFFER上,以防止过错修正极点缓冲方针的呼应装备

接下来咱们将介绍glVertexAttribPointer怎么界说数据的格局
从之前的极点着色器代码中咱们能够看到每个极点特点都有一个对应的数据类型如vec3、vec2对应着3位float与2位float组成的向量。咱们当然能够用整型组成向量如ivec3,或许双精度浮点数如dvec4(仅在OpenGL4.1中可用)。在OpenGL中,咱们经过运用不同的函数来对应不同的数据类型。

glVertexAttribPointer --> float
glVertexAttribIPointer --> int
glVertexAttribLPointer --> double

然后是这些函数的第二个参数size,其能够为1-4中的恣意整数,对应特点向量的1-4维度,这个参数表明每次从缓冲区数组中接连读取几个数据,这个数据不一定需求严厉对应极点特点的维度数,当极点维度数小于size时,剩余的位数会被疏忽,大于size时会从(0,0,0,1)取对应位数来弥补。

当size = 4,极点特点为vec3类型
缓冲区读取(0.1,0.2,0.3,0.4)
实践aPos = (0.1,0.2,0.3)

当size = 3, 极点特点为vec4类型
缓冲区读取(0.1,0.2,0.3)
实践aPos = (0.1,0.2,0.4,1.0)
Note:关于双精度类型不成立,短少的维度会是undefined value

接着是函数的第3个变量type,对应着缓冲区存储数据的类型,当这些数据被极点着色器读取时会转化成其所需求的类型。这儿简略和之前的特点类型弄混,其逻辑关系如下:

vec有必要运用glAttribPointer进行装备
ivec有必要运用glAttribIPointer进行装备
dvec有必要有必要运用glAttribLPointer进行装备
函数类型决议输出的数据类型

glAttribPointer的type可设定为:GL_HALF_FLOAT,GL_FLOAT,GL_DOUBLE,GL_FIXED ...
glAttribIPointer的type可设定为:GL_BYTE,GL_UNSIGNED_BYTE,GL_SHORT ...
glAttribLPointer的type可设定为:GL_DOUBLE ...
type决议了在缓存中存储数据的类型,当从缓存中读取的时分,不同的type到对应的特点数据类型有各自的转化办法。比方glVertexAttribPointer到normalized参数决议了,当type为整型时是否运用integer normalization将整型转化为单精度浮点类型

其间glAttribPointer的size类型除了能够为1-4之间的整数之外还能够为GL_BGRA,其实践上也是一个4位数据,可是前三位是逆序读取的,这是为了和Direct3D中的特定类型进行匹配,然后便利将D3D的部分数据转化到OpenGL中,相关细节详见OpenGL wiki

最终一部分是函数的stride和offset参数,这两个参数从哪里开端读取极点数据,以及读取完一个极点数据后怎么移动指针到下一个极点数据。详细来说stride表明从当时极点数据的最初到下一个极点数据最初有多少字节,当stride为0时,OpenGL默许这些数据严密摆放,其依据size和type自动计算到下一个数据的字节数,stride则为size * sizeof(type)。offset表明开端读取的指针方位,源于一些前史原因其类型为(void),咱们能够经过类型转化来将其他类型的指针转化为void

经过灵敏设置这些参数,咱们能够完结不同的数据贮存办法, 最经典的一种运用即交织存储

一般opengl更喜爱交织存储,由于咱们能够一次性获取单个极点所需的一切数据,而不需求从多个内存方位读取数据,相似cpu中的内存读取机制,这种数据拜访办法会有更少的cache miss,更高的功率。可是关于需求常常更改的数据和静态数据一起存在的状况来说,这种存储办法是不抱负的,比方咱们或许期望将常常更改的数据放在高速写入的内存区,而将静态数据放在其他当地。此刻咱们能够讲两种数据分隔存储,但关于静态数据而言,咱们仍应尽或许运用交织存储的办法。

3.2 VAO(Vertex Array Buffer)

VBO用于保存极点特点对应数据存储的极点缓冲区,能够经过它设置极点缓冲区的数据格局的解说。因而,可是这种解说并非存储在VBO中,而是针对当时OpenGL状况的一种设置,这意味着当咱们想要运用另一种VBO以及另一种格局解说时需求从头进行数据格局的设置,这种操作过于繁琐。为了处理这一问题,OpenGL中呈现了VAO这一概念

关于VAO,OpenGL wiki给出这样的解说

Vertex Array Object (VAO) is an OpenGL Object that stores all of the state needed to supply vertex data (with one minor exception noted below). It stores the format of the vertex data as well as the Buffer Objects (see below) providing the vertex data arrays.
一个极点数组方针是一个OpenGL方针,其保存供给极点数据所需的一切状况。它保存极点数据的格局以及供给极点数据数组的缓冲方针

从这个解说中咱们能够看到,VAO的效果实践上便是保存咱们在VBO中所做的操作,然后使咱们能够便利的复用VBO以及数据格局设置。但一起,OpenGL wiki中还给出了一个主见事项,较为重要

Note that a VAO merely references the buffers, it does not copy or freeze their contents; if referenced buffers are modified later, those changes will be seen when using the VAO.

VAO中所保存的是缓冲的引证,而非对其内容的仿制,这意味着当咱们在其他当地修正了这些缓冲,那么保存其引证的VAO在运用时也会运用这些改动

VAO相同归于OpenGL方针,因而其也有对应的创立以及毁掉函数

glGenVertexArrays
glCreateVertexArrays (only available in gl4.5)
glDeleteVertexArrays

与之前的VBO不同点在于其绑定函数,VBO绑定其object name到GL_ARRAY_BUFFER方针,而VAO的绑定则不需求绑定到其他方针上

glBindVertexArray(GLuint name)

这意味着,OpenGL能够一起运用多个绑定至不同方针的缓冲方针,而极点数组方针一起只能运用一个。留意,VAO不能在OpenGL上下文之间同享。

极点着色器拜访极点数据是经过VAO进行的,因而咱们需求经过VAO来设置是否激活某个极点特点,他们经过如下接口完结

void glEnableVertexAttribArray(Gluint index);
void glDisableVertexAttribArray(GLuint index);

或许经过DSA(Direct State Access)来设置

void glEnableVertexArrayAttrib(GLuint vao,Gluint index);
void glDisableVertexArayAttrib(GLuint vao,GLuint index);

DSA是gl4.5之后参加的新特性,能够在不讲OpenGL方针绑定至上下文的状况下修正方针状况, 假如运用旧版别,则有必要在此之前运用glBindVertexArray绑定至当时上下文。

一起,咱们还需留意,当对应的特点未被激活是,极点着色器在拜访对应特点时会取得一个默许值,而非报错退出。

并且,OpenGL的compatibility profile中会将name为0的VAO方针设置为一个默许方针,这意味着在不自动绑定任何VAO时,一切修正VAO状况的操作都会默许运用到VAO 0上。而关于core profile,VAO 0并不是一个方针,所以在手动绑定一个VAO方针之前,咱们不能运用任何修正VAO状况的操作。

VAO的运用相对简略,只需在制作之前,将对应VAO绑定至当时上下文,即可运用其对应的VBO和数据格局装备

while(!glShouldWindowClose(window))}{
	glBindVertexArray(VAO);
	// draw call
	... ...
}

3.3 Index buffers

最终咱们介绍一下index buffer。之前在介绍Vertex Specification的进程中咱们介绍过运用索引数组来决议制作次序以及进行数据压缩,而这则是经过index buffer完结的。

index buffer本身和VBO相一起Buffer Object, 只是经过绑定到GL_ELEMENT_ARRAY_BUFFER来将其当作索引数组来运用,其他操作和VBO相同。要运用这个索引缓冲方针,咱们还需求在制作时运用glDrawElements而不是DrawDrawArrays。

4 总结

咱们能够从VBO现已index buffer中更进一步的了解buffer object,buffer object本身只是用来存储数据,规则数据的类型以及缓冲区巨细。而这个buffer object的详细效果由绑定到的方针决议。绑定到GL_ARRAY_BUFFER是VBO,GL_ELEMENT_ARRAY_BUFFER则是index buffer。极点数据格局设置的操作则是对OpenGL状况机进行操作,而这些绑定以及格局设置,又被绑定的VAO记载,然后能够便利的运用这些设置。

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=698

标签: OpenGLGame Dev
分享给朋友:

“LearnOpenGL 笔记 -- VAO & VBO” 的相关文章

使用1panel布置前后端别离项目 Java代码打包 前端打包

使用1panel布置前后端别离项目 Java代码打包 前端打包

初度发布于我的个人文档 参阅: 1.1Panel 官方文档 本文介绍一下怎么使用1panel布置一个简略的前后端别离项目。 1,具有一个Liunx服务器 第一步是购买一个Linux服务器,能够买一台线下实在的机器+公网IP或买一个阿里云、腾讯云、京东云、华为云服务器。 2.装置1panel 参阅1p...

开源证劵,深耕金融领域,助力实体经济发展

开源证劵,深耕金融领域,助力实体经济发展

开源证券股份有限公司成立于1994年2月,总部位于陕西省西安市。公司经过多次增资扩股和股份制改造,坚定不移地走市场化道路,并持续推进“人才领先,创新为魂”的发展战略,实现了持续、快速、健康发展。目前,开源证券在全国范围内拥有70家分支机构,完成了证券、基金、期货、私募股权投资等多业态金融控股布局,成...

区块链的应用,金融领域

区块链的应用,金融领域

1. 金融行业: 数字货币:如比特币、以太坊等,是区块链最直接的应用。 跨境支付:通过区块链技术,可以实现快速、低成本的跨境支付。 供应链金融:区块链可以用于记录和追踪供应链中的交易,提高透明度和效率。2. 供应链管理: 追踪和溯源:区块链可以记录产品从生产到销售的整个过程,...

开源爬虫,助力数据获取与处理的利器

开源爬虫,助力数据获取与处理的利器

3. MechanicalSoup 特点:MechanicalSoup 是一个 Python 库,旨在模拟人类在使用浏览器时与网站的交互,基于 Python 的 Requests 和 BeautifulSoup 构建。 优点:适合模拟人类与网站的交互,支持 HTTP 会话和文档导航。...

云计算好学吗,云计算好学吗?入门与进阶指南

云计算好学吗,云计算好学吗?入门与进阶指南

云计算是一个涉及广泛技术和概念领域的学科,包括但不限于网络、存储、服务器、软件、安全、自动化和优化等。对于初学者来说,学习云计算可能会觉得有一定的难度,因为它需要理解许多抽象的概念和技术细节。随着实践经验的积累和不断的学习,学习云计算的过程会变得更加容易。1. 基础知识:首先,确保你具备计算机科学的...

项目管理系统开源,助力高效项目管理

项目管理系统开源,助力高效项目管理

1. Redmine 特点:基于Ruby on Rails框架,支持多种项目管理功能,如问题跟踪、甘特图、日历、 wiki等。 适用场景:适合需要灵活配置和定制化的团队。2. Taiga 特点:基于Python Django框架,支持敏捷项目管理方法,如Scrum和Kanban,提...