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

android openGL ES详解——缓冲区VBO/VAO/EBO/FBO

目录

一、缓冲区对象概念

二、分类

三、顶点缓冲区对象VBO

1、概念

2、为什么使用VBO

3、如何使用VBO

生成缓冲区对象

绑定缓冲区对象

输入缓冲区数据

更新缓冲区中的数据

删除缓冲区

4、VBO应用

四、顶点数组对象VAO

1、概念

2、为什么使用VAO

3、如何使用VAO

生成VAO

绑定VAO

解绑VAO

删除VAO

4、VAO应用

五、索引缓冲区对象EBO/IBO

1、概念

2、为什么使用IBO/EBO

3、如何使用EBO

生成EBO

绑定EBO

输入数据到缓冲区

链接属性

绘制

4、EBO应用

5、绘制过程

六、总结

1、常用各类缓冲区分析

2、无法删除缓冲区

七、源码下载


一、缓冲区对象概念

缓冲区对象(Buffer Object)是OpenGL中用于在GPU内存中存储数据的一种机制,它们使得数据传输和渲染更加高效。

二、分类

常用的缓冲区分类如下:

VAO(Vertex Array Object): 顶点数组对象,用于存储顶点数据
VBO(Vertex Buffer Object): 顶点缓冲对象,用于存储顶点数据
EBO(Element Buffer Object): 元素缓冲对象,用于存储顶点的索引数据
IBO(Index Buffer Object): 索引缓冲对象,用于存储索引数据

FBO(Index Buffer Object):帧缓冲对象,用于渲染到纹理或离屏渲染。

PBO(Pixel Buffer Object):像素缓冲区对象

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

应用程序对顶点属性数据和元素索引使用顶点缓冲区对象。

三、顶点缓冲区对象VBO

1、概念

VBO(Vertex Buffer Object)顶点缓冲对象,是在显卡存储空间(即,GPU内存)中开辟的一块区域,在显卡存储空间中开辟一块区域,用于存放顶点的各类属性信息。如顶点坐标、纹理坐标、顶点颜色等数据。
在渲染时直接从显VBO去取数据而不必与CPU进行数据交换。

2、为什么使用VBO

什么是顶点缓冲区对象,为什么要使用它们

解释1:

将对象数据存储在客户端内存中,只有在渲染时将其传输到GPU内存(即,显存)中。没有大量数据传输时,这很好,但随着我们的场景越来越复杂,会有更多的物体和三角形,这会给CPU和内存增加额外的成本。

我们能做些什么呢?我们可以使用顶点缓冲对象,而不是每帧从客户端内存传输顶点信息,信息将被传输一次,然后渲染器将从该图形存储器缓存中得到数据。

解释2:

直接只用顶点数组来绘图,在客户端制定顶点数据,每次绘制时从先从内存中加载这些数据,这样会带来绘制延时,因此我们想着在显存中开辟一块区域,在每次绘制间隔就将顶点数据加载过来,这样绘制时直接读取显存中的数据就可以了,明显提高渲染速度。于是,我们需要引入顶点缓冲区。
一般在两种不同处理速度的物理组件之间进行数据传输时都要用到缓冲区,OpenGL由于需要处理内存与GPU的数据传输,也要用到一系列缓冲区对象,顶点缓冲区只是其中之一。在顶点数组的基础上使用定点缓冲区可以提高渲染速率。

解释3:

VBO允许开发者将大量顶点数据从CPU传输到GPU,减少每帧的CPU-GPU通信量,提高渲染效率。缓冲区对象通过在 GPU 上存储数据,提高了数据传输和渲染的效率。

使用顶点数组指定的顶点数据保存在客户内存中。在进行 glDrawArrays或者
glLDrawElements 等绘图调用时,这些数据必须从客户内存复制到图形内存。但是,如果我们没有必要在每次绘图调用时都复制顶点数据,而是在图形内存中缓存这些数据,那就好得多了。这种方法可以显著地改进渲染性能,也会降低内存带宽和电力消耗需求,对于手持设备相当重要。这是顶点缓冲区对象发挥作用的地方。

顶点组冲区对象使应用程序可以在高性能的图形内存中分配和缓存顶点数据,并从这个内存进行泻染,从而避免在每次绘制图元的时候重新发送数据。不仅是项点数据,描述图元顶点索引、作为 glDrawElements 参数传递的元素索引也可以缓存。

解释4:

通过顶点缓冲对象(Vertex Buffer Objects, VBO)管理这个内存,它会在GPU内存(通常被称为显存)中储存大量顶点。使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上,而不是每个顶点发送一次。

从CPU把数据发送到显卡相对较慢,所以只要可能我们都要尝试尽量一次性发送尽可能多的数据。当数据发送至显卡的内存中后,顶点着色器几乎能立即访问顶点,这是个非常快的过程。

解释5:

为什么需要使用VBO

将顶点数据保存在内存中,在调用glDrawArrays或者glDrawElements等绘制方法前需要调用相应的方法将数据送入显存,I/O开销大,性能不够好。
若采用顶点缓冲区对象存放顶点数据,则不需要在每次绘制前都将顶点数据复制进显存,而是在初始化顶点缓冲区对象时一次性将顶点数据送入显存,
每次绘制时直接使用显存中的数据,可以大大提高渲染性能。

3、如何使用VBO

顶点缓冲区的使用需要经历一下步骤:


生成缓冲区对象
glGenBuffers(GLsizei n, GLuint* buffers);

分配n个缓冲区对象标识符,返回的标识符存储在buffers数组中。每一个标识符表示一个已经开始使用的缓冲区对象。具体如何分配有OpenGL内部决定,与调用者无关。此外,该方法调用之后在buffers中存储的标识符并不一定是连续的数字,而且0作为一个被保留的缓冲区对象名称,该方法从来不会返回0标识符。

绑定缓冲区对象
glBindBuffer(GLenum target, GLuint buffer);

绑定缓冲区对象。OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。buffer是glGenBuffers()返回的标识符数组的一个值,target表示改缓冲区应该绑定为什么类型的缓冲区对象,有GL_ARRAY_BUFFER(顶点数组缓冲区)、GL_ELEMENT_ARRAY_BUFFER(索引数组缓冲区)等。需要注意的是(以GL_ARRAY_BUFFER为例):

  1. 如果buffer为0,则停用顶点数组缓冲区;
  2. 如果buffer为非零整数,且buffer代表的缓冲区之前未绑定过,则创建一个新的缓冲区对象和buffer相对应;
  3. 如果buffer之前绑定过,则激活buffer对应的缓冲区。

OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。OpenGL允许我们同时绑定多个缓冲,只要它们是不同的缓冲类型。我们可以使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上。

输入缓冲区数据
glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);

初始化缓冲区对象,并指定缓冲区数据。target和glBindBuffer()中的target一样,data表示要写入到缓冲区的数据数组头指针,size表示data数组的字节数,usage表示数据在分配到缓冲区之后如何进行读取和写入,主要用来提供性能,指定了我们希望显卡如何管理给定的数据。其值为“GL_频率_操作”格式,有

GL_STREAM_DRAW、

GL_STREAM_READ、

GL_STREAM_COPY、

GL_STATIC_*、

GL_DYNAMIC_*。

其中,“频率”指缓冲区数据的读取或者渲染速率,有流模式、静态模式,动态模式:

  • STREAM:流模式,当缓冲区中的数据更新频率高,但使用频率低时使用;
  • STATIC:静态模式,当缓冲区中的数据只需制定一次,但使用频率比较高时使用;
  • DYNAMIC:动态模式,当缓冲区中的数据更新频率高,并且使用频率高时使用。

“操作”有绘制 、读取和拷贝:

  • DRAW:绘制,缓冲区中的数据直接用于渲染,也就是内存中的数据直接作为渲染数据;
  • READ:读取,缓冲区中的数据主要用于应用程序的计算,而不是直接作用于渲染;
  • COPY:拷贝,缓冲区中的数据对渲染来说是只读数据,需要拷贝后将拷贝数据作为渲染数据。

例如,如下三种形式的含义:

  • GL_STATIC_DRAW 数据不会或几乎不会改变。仅在显存上进行读取,不进行写入,适用于静态数据。
  • GL_DYNAMIC_DRAW数据会被改变很多。仅在显存上进行读取,并允许写入,数据经常改变。
  • GL_STREAM_DRAW 数据每次绘制时都会改变。仅在显存上进行读取,不进行写入,数据每次绘制都改变。

一般情况下,位置数据不会改变,每次渲染调用时都保持原样,所以它的使用类型最好是GL_STATIC_DRAW。如果,比如说一个缓冲中的数据将频繁被改变,那么使用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,这样就能确保显卡把数据放在能够高速写入的内存部分。现在我们已经把顶点数据储存在显卡的内存中,用VBO这个顶点缓冲对象管理。

更新缓冲区中的数据
glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);

更新缓冲区中的数据。target和之前两个方法的参数一样,data指写入缓冲区的数据所在数组头指针,size指data数组中要写入缓冲区的数据的字节数,offset指要写入缓冲区的第一个数据在data数组的位置。也即将data数组中从offset(字节为单位)开始的size个字节写入缓冲区。

删除缓冲区
glDeleteBuffers(GLsizei n, const GLuint* buffers);

删除buffers中前n个缓冲区对象,他们的名称就是buffers数组中的元素。

4、VBO应用

创建VBO四步:
val buffers = IntArray(1)
//1.生成一个VBO,并ID存储在VBO中,此时VBO拥有了其唯一的id
GLES20.glGenBuffers(buffers.size, buffers, 0)
if (buffers[0] == 0) {Log.d(TAG, "can not create a new vertex buffer object.")
}
bufferId = buffers[0]
//2.绑定缓冲区
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0])
val vertexArray = ByteBuffer.allocateDirect(indexData.size * BYTES_PER_SHORT).order(ByteOrder.nativeOrder()).asShortBuffer().put(indexData)
vertexArray.position(0)
//3.将顶点数据复制到缓冲中,并指定数据用以静态访问,
将顶点数据上传到GPU内存中,以便后续的渲染操作。
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,vertexArray.capacity() * BYTES_PER_SHORT,vertexArray,GLES20.GL_STATIC_DRAW
)
//4.将buffer id设置为0,解绑缓冲区
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0)

四、顶点数组对象VAO

1、概念

VAO(vertex Array Object):顶点数组对象。

注意: VAO是OpenGL ES 3.0之后才推出的新特性, 所以在使用VAO前需要确定OpenGL ES的版本是否是3.0之后的版本。

顶点数组对象可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中。

VAO用于保存顶点属性数组的状态,包括与VBO的绑定以及顶点属性的配置。

2、为什么使用VAO

解释1:

顶点属性存储有2种方式:客户端顶点数组顶点缓冲区对象。VAO使得客户端顶点数组的数据传入VBO的速度加快。

通过VAO,开发者可以一次性配置好顶点数组的所有状态,在后续的绘制中快速恢复这些状态,减少OpenGL函数调用次数,提升效率。

解释2:

在上面VBO的介绍中我们知道每次在绘制的时候都需要频繁地绑定与解绑VBO,每次绘制还需要取出VBO中的数据进行赋值之后才能进行绘制渲染。当数据量大的时候,重复这些操作就会变得很繁琐。通过VAO就可以简化这个过程,因此VAO可以简单理解成VBO的管理者,避免在帧绘制时再去手动操纵VBO,VAO不能单独使用,
需要搭配VBO使用。

对于GPU来说VBO就是一堆数据,但是这堆数据怎么解析使用,需要glEnableVertexAttribArray等相关函数在每次绘制的时候告诉GPU,那么VAO的作用就是简化这个过程的,只需要在初始化的时候将这些解析逻辑与VAO绑定一次即可,
然后每次在绘制的时候只需绑定对应的VAO,而不必每次再绑定VBO,然后去告诉GPU如何解析相关数据了,可以说是一个性能的优化了。

解释3:
当我们再绘制不同的顶点数据或者是其他数据时,我们需要重新进行一遍绑定设置等等操作来完成绘制不同,这时候就引出顶点数组对象。

在OpenGL中,顶点数组对象(Vertex Array Object,VAO)是一种OpenGL对象,用于存储顶点数据的状态信息,包括顶点坐标、法线、纹理坐标等。VAO可以看作是一种包含了多个顶点属性配置的容器,使得你可以在绘制时轻松地切换和使用不同的顶点数据。

顶点数组对象的使用可以帮助你更有效地组织和管理顶点数据,特别是在绘制多个物体或者在渲染循环中多次切换不同的顶点数据时。

其实可能还不太明白,其实使用的时候确实都是要走绑定顶点缓冲区,然后设置顶点的布局等等,但是我们现在把上面一套流程放进了一个叫VAO的对象中,也就是生成绑定它就相当于走完了内部的一套流程,在换顶点数据或者顶点布局的时候,就不需要再写一遍这么麻烦了,相当于是封装了一层,我们后续可以看看其实也是真的要封装一层的,我们可以通过在换顶点布局或者顶点数据的时候体现流程:

老一套的流程是:

1.解绑并重新绑定新的着色器程序

glUseProgram(0);

glUseProgram(shaderID);

2.解绑和重新绑定顶点缓冲区

glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindBuffer(GL_ARRAY_BUFFER, buffer));

3.重新设置顶点的布局

glEnableVertexAttribArray(0);

glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);

4.绑定我们的索引缓冲区

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));

5.绘制调用glDrawElements

引入VAO之后,我们要更改数据就变成了这样:

1.解绑并重新绑定新的着色器程序

glUseProgram(0);

glUseProgram(shaderID);

2.绑定顶点数组(包含了绑定顶点缓冲区,设置顶点的布局,方便直接绑定别的,可以切换)glBindVertexArray(vao);
3.绑定索引缓冲glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
4.绘制调用glDrawElements

另外这个顶点数组对象对于OpenGL是强制性的,因为即使我们没有创建它,还是走的老一套流程,其实状态还是由顶点数组对象来维护的。

3、如何使用VAO

生成VAO
unsigned int VAO;
glGenVertexArrays(1, &VAO);
绑定VAO
glBindVertexArray(VAO)
解绑VAO
glBindVertexArray(0)
删除VAO
glDeleteVertexArrays

4、VAO应用

// 生成VAO和VBO
GLuint vao;
glGenVertexArrays(1, &vao);
GLuint vbo;
glGenBuffers(1, &vbo);// 绑定VAO
glBindVertexArray(vao);// 绑定VBO并设置顶点数据
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 配置顶点属性,用于将当前的顶点属性与顶点缓冲对象(VBO)关联起来
//配置顶点属性指针: 使用 glVertexAttribPointer 函数配置顶点属性指针,告诉OpenGL如何解释顶点数据。
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);// 解绑VAO和VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

五、索引缓冲区对象EBO/IBO

1、概念

IBO/EBO, Element Buffer Objec,索引缓冲区对象。EBO用于存储顶点的索引数据,允许通过索引引用顶点,避免重复存储相同的顶点数据。

索引缓冲区和顶点缓冲区类似,也是显存上的一段内存,只不过上面的数据用处不同,索引缓冲区故名思义里面的数据是用于索引,主要作用是用于复用顶点缓冲区里的数据。

使用的GL_ELEMENT_ARRAY_BUFFER表示索引缓冲区。

2、为什么使用IBO/EBO

解释1:

EBO提高了渲染效率,减少了顶点数据的冗余,尤其在渲染复杂网格时,通过索引复用顶点能够显著节省内存。

解释2:

OpenGL渲染都是渲染三角形,如果我们想渲染一个正方形,就要通过渲染两个三角形,拼接成一个正方形,那么这两个三角形有两个顶点是重合的,如果没有索引缓冲区,两个三角形则需要六个顶点,而实际上一个正方形只有四个顶点,这里有两个顶点时数据冗余。仅仅一个正方形就有这么多冗余,那么一个复杂的游戏场景就会浪费非常多的内存。

解释3:

为什么使用EBO

正常画一个立方体,需要8个顶点,合计6个面。

但实际我们是用了4*6=24个点来画的,因为4个点组成一个面,我们能看出来,有很多重复点,为了能节省内存空间,我们想只用8个点描画立方体,这需要用到EBO

VertexData vc[] = {//正面{QVector3D(-0.5f, 0.0f, 0.5f), QVector3D(1.0f, 0.0f, 0.0f)},     //1{QVector3D(0.5f, 0.0f, 0.5f), QVector3D(0.0f, 1.0f, 0.0f)},      //2{QVector3D(0.5f, 1.0f, 0.5f), QVector3D(0.0f, 0.0f, 1.0f)},      //3{QVector3D(-0.5f, 1.0f, 0.5f), QVector3D(1.0f, 1.0f, 1.0f)},     //4//右面{QVector3D(0.5f, 0.0f, 0.5f),  QVector3D(0.0f, 1.0f, 0.0f)},     //2{QVector3D(0.5f, 0.0f, -0.5f), QVector3D(1.0f, 1.0f, 0.0f)},     //5{QVector3D(0.5f, 1.0f, -0.5f), QVector3D(0.0f, 1.0f, 1.0f)},     //6{QVector3D(0.5f, 1.0f, 0.5f),  QVector3D(0.0f, 0.0f, 1.0f)},     //3//左面{QVector3D(-0.5f, 0.0f, 0.5f), QVector3D(1.0f, 0.0f, 0.0f)},     //1{QVector3D(-0.5f, 0.0f, -0.5f),QVector3D(1.0f, 0.0f, 1.0f)},     //8{QVector3D(-0.5f, 1.0f, -0.5f),QVector3D(1.0f, 0.6f, 0.0f)},     //7{QVector3D(-0.5f, 1.0f, 0.5f), QVector3D(1.0f, 1.0f, 1.0f)},     //4//背面{QVector3D(0.5f, 0.0f, -0.5f),   QVector3D(1.0f, 1.0f, 0.0f)},   //5{QVector3D(0.5f, 1.0f, -0.5f),   QVector3D(0.0f, 1.0f, 1.0f)},   //6{QVector3D(-0.5f, 1.0f, -0.5f),  QVector3D(1.0f, 0.6f, 0.0f)},   //7{QVector3D(-0.5f, 0.0f, -0.5f),  QVector3D(1.0f, 0.0f, 1.0f)},   //8//顶面{QVector3D(0.5f, 1.0f, 0.5f),    QVector3D(0.0f, 0.0f, 1.0f)},   //3{QVector3D(0.5f, 1.0f, -0.5f),   QVector3D(0.0f, 1.0f, 1.0f)},   //6{QVector3D(-0.5f, 1.0f, -0.5f),  QVector3D(1.0f, 0.6f, 0.0f)},   //7{QVector3D(-0.5f, 1.0f, 0.5f),   QVector3D(1.0f, 1.0f, 1.0f)},   //4//底面{QVector3D(0.5f, 0.0f, 0.5f),    QVector3D(0.0f, 1.0f, 0.0f)},   //2{QVector3D(0.5f, 0.0f, -0.5f),   QVector3D(1.0f, 1.0f, 0.0f)},   //5{QVector3D(-0.5f, 0.0f, -0.5f),  QVector3D(1.0f, 0.0f, 1.0f)},   //8{QVector3D(-0.5f, 0.0f, 0.5f),   QVector3D(1.0f, 0.0f, 0.0f)},   //1};

EBO原理

//顶点有8个
VertexData vcs[] = {//正面{QVector3D(-0.5f, 0.0f, 0.5f), QVector3D(1.0f, 0.0f, 0.0f)},     //1{QVector3D(0.5f, 0.0f, 0.5f), QVector3D(0.0f, 1.0f, 0.0f)},      //2{QVector3D(0.5f, 1.0f, 0.5f), QVector3D(0.0f, 0.0f, 1.0f)},      //3{QVector3D(-0.5f, 1.0f, 0.5f), QVector3D(1.0f, 1.0f, 1.0f)},     //4{QVector3D(0.5f, 0.0f, -0.5f), QVector3D(1.0f, 1.0f, 0.0f)},     //5{QVector3D(0.5f, 1.0f, -0.5f), QVector3D(0.0f, 1.0f, 1.0f)},     //6{QVector3D(-0.5f, 1.0f, -0.5f),QVector3D(1.0f, 0.6f, 0.0f)},     //7{QVector3D(-0.5f, 0.0f, -0.5f),QVector3D(1.0f, 0.0f, 1.0f)},     //8};
//索引 对应8个面,每个面说明使用哪4个顶点GLuint indices[] = { // 起始于0!0, 1, 2, 3, // face 11, 4, 5, 2,  // face 20, 7, 6, 3,  // face 34, 5, 6, 7,  // face 42, 5, 6, 3,  // face 51, 4, 7, 0,  // face 6};

3、如何使用EBO

生成EBO
 glGenBuffers(1, &EBO);
绑定EBO
 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
输入数据到缓冲区
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
链接属性
glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid*)0);
glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid*)sizeof(QVector3D));
绘制
/*
glDrawElements是用于渲染索引缓冲区,
第一个和其他DrawCall一样,
第二个参数是count,表示有多少顶点需要渲染,
第三个参数是索引缓冲区参数类型,必须是GLES30.GL_UNSIGNED_SHORT或者GL_UNSIGNED_BYTE,
第四个参数是offset。
*/
GLES30.glDrawElements(GLES30.GL_TRIANGLES, count, type, offset)

PS: 绑定和解绑的顺序很重要,勿更改,另外EBO不用解绑,就算要解绑,也要在VAO解绑后。

4、EBO应用

//1 使用glGenBuffers函数生成一个缓冲ID    
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
//2 绑定vao
glBindVertexArray(VAO);
//3 使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER缓冲类型上
glBindBuffer(GL_ARRAY_BUFFER, VBO); //(绑定和解绑的顺序很重要,勿更改)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
//4 把用户定的义数据复制到当前绑定缓冲的函数
glBufferData(GL_ARRAY_BUFFER, sizeof(vcs), vcs, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//5 链接顶点属性//indx: 属性名//size: 顶点大小//type: 数据类型//normalized:数据被标准化//stride: 步长//ptr: 数据在缓冲中起始位置的偏移量
glVertexAttribPointer(m_posAttr, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid*)0);
glVertexAttribPointer(m_colAttr, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (GLvoid*)sizeof(QVector3D));
//6 解绑缓存(绑定和解绑的顺序很重要,勿更改)
glBindBuffer(GL_ARRAY_BUFFER, 0);
//7 解绑VAO
glBindVertexArray(0);

5、绘制过程

//1 绑定vao
glBindVertexArray(VAO);
//2 开启顶点属性
glEnableVertexAttribArray(0);
//颜色值
glEnableVertexAttribArray(1);
//3 绘制四边形,24个索引值
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, (GLvoid*)0);
//4 停用对应的顶点属性数组
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
//5 解绑VAO
glBindVertexArray(0);

六、总结

1、常用各类缓冲区分析

VBO: 存储大量顶点,因而可以利用VBO一次性发送大量数据到显卡
VAO: 配置并告诉了OpenGL如何使用VBO,以及使用哪个VBO

EBO/IBO:

索引缓冲区里面就是存储了一系列索引,用于复用顶点缓冲区。
有个问题,大家可能会想之前用6个顶点也不过18个数,使用了顶点缓冲区后变为12个,但是索引缓冲区还有6个,好像没有节省多少内存。
但是实际上顶点可能包含了非常多的数据,比如我们之前用它来存颜色,它还可以存纹理等额外数据,实际应用场景一个顶点可能有非常多数据,所以复用可节省的内存是非常可观的。

2、无法删除缓冲区

OpenGL并没有提供直接删除缓冲区的函数或方法。这是因为OpenGL的设计理念是尽可能地提供高性能和灵活性,而不是提供高级别的内存管理功能。因此,开发者需要手动管理缓冲区的生命周期。

要删除缓冲区,开发者需要按照以下步骤进行操作:

  1. 解绑缓冲区:在删除缓冲区之前,需要先解绑缓冲区。可以使用glBindBuffer函数将当前的缓冲区绑定到一个无效的缓冲区对象上,例如glBindBuffer(GL_ARRAY_BUFFER, 0)

  2. 删除缓冲区对象:使用glDeleteBuffers函数来删除缓冲区对象。该函数接受一个整数参数,表示要删除的缓冲区对象的数量,以及一个指向缓冲区对象的指针。例如,glDeleteBuffers(1, &bufferObject)将删除名为bufferObject的缓冲区对象。

需要注意的是,删除缓冲区对象并不会自动释放缓冲区所占用的内存。开发者需要在删除缓冲区对象之前,确保已经释放了缓冲区所占用的内存,以避免内存泄漏。

七、源码下载

VAO顶点数组对象应用演示demo源码下载:

https://download.csdn.net/download/github_27263697/89915626

VBO顶点缓冲区对象应用演示demo源码下载: 

https://download.csdn.net/download/github_27263697/89915617

EBO/IBO索引缓冲区对象应用演示demo源码下载:

https://download.csdn.net/download/github_27263697/89916753


参考文章

OpenGL入门(四)顶点缓冲区_顶点缓冲器有什么用-CSDN博客

Android OpenGL ES 2.0(七)--- 顶点缓冲区对象-CSDN博客

OpenGL 入门(二)— 顶点数组对象(VAO)和顶点缓冲对象(VBO)_glvertexattribpointer-CSDN博客

你好,三角形 - LearnOpenGL CN

https://segmentfault.com/a/1190000042610598?sort=newest#item-1

OpenGL学习总结-数据缓存区(四)_opengl genbuffer-CSDN博客

一篇搞懂OpenGL uniforms变量和顶点数组对象(Vertex Array Object,VAO)_glgenvertexarrays-CSDN博客OpenGL无法删除缓冲区_OpenGL/OpenTK -多帧缓冲区_使用OpenGL内插数据缓冲区? - 腾讯云开发者社区 - 腾讯云小白学opengl 第五课 之 索引缓冲对象 - 踏月清风 - 博客园OpenGL无法删除缓冲区_OpenGL/OpenTK -多帧缓冲区_使用OpenGL内插数据缓冲区? - 腾讯云开发者社区 - 腾讯云一篇搞懂OpenGL uniforms变量和顶点数组对象(Vertex Array Object,VAO)_glgenvertexarrays-CSDN博客
OpenGL ES 索引缓冲区(4)_opengl索引缓冲区-CSDN博客
 

相关文章:

android openGL ES详解——缓冲区VBO/VAO/EBO/FBO

目录 一、缓冲区对象概念 二、分类 三、顶点缓冲区对象VBO 1、概念 2、为什么使用VBO 3、如何使用VBO 生成缓冲区对象 绑定缓冲区对象 输入缓冲区数据 更新缓冲区中的数据 删除缓冲区 4、VBO应用 四、顶点数组对象VAO 1、概念 2、为什么使用VAO 3、如何使用VAO…...

计算机网络——传输层服务

传输层会给段加上目标ip和目标端口号 应用层去识别报文的开始和结束...

gin入门教程(8):渲染与静态文件

目录结构 /hello-gin │ ├── cmd/ │ └── main.go ├── pkg/ │ └── shared_lib.go ├── internal/ │ └── internal_lib.go ├── api/ │ └── routes.go ├── config/ │ └── config.go ├── migrations/ │ └── migration.sql └…...

Fast Simulation of Mass-Spring Systems in Rust 论文阅读

参考资料: 文章目录 概述流程概述:1.前置知识1.1 运动方程(牛顿第二定律)1.2 二阶导数的离散化1.3 代入运动方程1.4 物理意义 2. 将隐式积分问题转化为一个优化问题2.1 要解的是隐式积分问题是:2.2 引入辅助变量d1. 左…...

javaWeb项目-ssm+vue志愿者招募网站功能说明介绍

本项目源码(点击下方链接下载):java-ssmvue志愿者招募网站实现源码(项目源码-说明文档)资源-CSDN文库 项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架:ssm、Springboot 前端&#xff1a…...

Selenium + Titanium代理获取请求的接口数据

有一个采集数据的需求,分析了页面数据后发现列表有一个id,但是没有其他数据,打开详情并不是通过id,而是其他一个字段,这就说明通过selenium抓取页面数据还不行,还要接口返回的数据。这个时候就需要用到代理…...

ELK Stack与Graylog:强大的日志分析和可视化工具

ELK Stack的使用方法 ELK Stack由Elasticsearch、Logstash和Kibana三个核心组件组成,它们协同工作,提供了从日志收集、解析、存储到可视化的完整解决方案。 安装与配置Elasticsearch Elasticsearch是ELK Stack的存储和查询引擎,负责存储日…...

安全见闻(6)——开阔眼界,不做井底之蛙

内容预览 ≧∀≦ゞ 安全见闻六:通讯协议安全问题剖析声明引言一、通讯协议的保密性问题二、通讯协议的完整性问题三、身份验证问题四、可用性问题五、通讯协议的实现问题六、协议设计缺陷七、移动通讯协议的安全问题八、物联网通讯协议的安全问题九、工业控制系统通…...

GRU神经网络理解

全文参考以下B站视频及《神经网络与深度学习》邱锡鹏,侧重对GPU模型的理解,初学者入门自用记录,有问题请指正【重温经典】GRU循环神经网络 —— LSTM的轻量级版本,大白话讲解_哔哩哔哩_bilibili 更新门、重置门、学习与输出 注&a…...

Windows 10、Office 2016/2019 和 PPTP 和 L2TP协议即将退役,企业应尽早做好准备

关心微软技术和产品的朋友一定对这个网站很熟悉:https://microsoftgraveyard.com/,这里静静的躺着很多微软技术和产品。近日,微软又在准备一场新的“告别仪式”了,这次是 Windows 10、Office 2016/2019 和一些老旧的协议与技术。让…...

论文阅读:Guided Linear Upsampling

今天介绍一篇有趣的文章,Guided Linear Upsampling,基于引导的线性上采样,这是发表在 ACM transaction on Graphic 的一篇工作。 Abstract 引导上采样是加速高分辨率图像处理的一种有效方法。在本文中,文章作者提出了一种简单而…...

深度图和RGB图对齐

坐标系间的转换_坐标系转换-CSDN博客 深度图与彩色图的配准与对齐_彩色 深度 配准-CSDN博客 kinect 2.0 SDK学习笔记(四)--深度图与彩色图对齐_mapdepthframetocolorspace-CSDN博客 相机标定(三)-相机成像模型_相机小孔成像模型…...

滑动窗口与TCP的缓冲区(buff)的关系

‌滑动窗口与TCP的缓冲区(buff)有直接关联。‌ 滑动窗口机制是TCP协议中用于流量控制和拥塞控制的重要机制。滑动窗口实际上是一个操作系统开辟的缓存空间,用于指定无需等待确认应答即可继续发送数据的最大值。这个窗口大小(win&…...

一款好用的搜索软件——everthing(搜索比文件资源管理器快)

everthing官网链接 在官网选择下载 1.下载后双击打开 2.点击OK(需要其他语言自己选择) 3.选择安装位置(路径最好别带中文和空格) 继续点击下一步 4. 点击下一步 5.继续点击安装 6.然后就完成了 7.点击打开然后就可以搜索了...

C#WPF的App.xaml启动第一个窗体的3种方式

WPF的App.xaml启动第一个窗体的3种方式 1.使用App.xaml的StartupUri属性启动&#xff08;推荐使用&#xff09; 在App.xaml文件中&#xff0c;你可以设置StartupUri属性来指定启动时显示的第一个窗口&#xff1a; <Application x:Class"浅看一眼WPF.App"xmlns&…...

【JAVA毕设】基于JAVA的酒店管理系统

一、项目介绍 本系统前端框架采用了比较流行的渐进式JavaScript框架Vue.js。使用Vue-Router实现动态路由&#xff0c;Ajax实现前后端通信&#xff0c;Element-plus组件库使页面快速成型。后端部分&#xff1a;采用SpringBoot作为开发框架&#xff0c;同时集成MyBatis、Redis、…...

聚类--机器学习西瓜书阅读笔记(六)

无监督学习&#xff1a;通过对无标记训练样本的学习&#xff0c;揭示数据内在规律和性质。 聚类试图将数据集中的样本划分为若干不相交的子集&#xff0c;聚类过程自动形成簇结构&#xff0c;簇对应的语义需要子集命名把握。 聚类过程可以作为单独的过程&#xff0c;寻找数据…...

OpenHarmony(1)开发环境搭建

一&#xff1a;开源项目 OpenHarmony是由开放原子开源基金会&#xff08;OpenAtom Foundation&#xff09;孵化及运营的开源项目&#xff0c;目标是面向全场景、全连接、全智能时代&#xff0c;基于开源的方式&#xff0c;搭建一个智能终端设备操作系统的框架和平台&#xff0…...

Triton服务在ASR语音识别系统中的实现

Triton服务在ASR语音识别系统中的实现 一、引言二、环境准备1. 硬件环境2. 软件环境 三、模型选择与训练1. 数据准备2. 模型架构3. 模型训练 四、模型转换与优化1. 模型转换2. 模型优化 五、配置Triton服务1. 安装Triton服务2. 创建模型仓库 一、引言 自动语音识别&#xff08…...

Typora一款极简Markdown文档编辑、阅读器,实时预览,所见即所得,多主题,免费生成序列号!

文章目录 Typora下载安装Typora序列号生成 Typora是一款Markdown编辑器和阅读器&#xff0c;风格极简&#xff0c;实时预览&#xff0c;所见即所得&#xff0c;支持MacOS、Windows、Linux操作系统&#xff0c;有图片和文字、代码块、数学公式、图表、目录大纲、文件管理、导入导…...

python机器人编程——用python调用API控制wifi小车的实例程序

目录 一、前言二、一个客户端的简单实现2.1 首先定义一个类及属性2.2 其次定义连接方法2.3 定义一些回调函数2.4 定义发送小车指令方法2.5 定义一个正常关闭方法 三、python编程控制小车的demo实现四、小结PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源p…...

面试学习整理-线程池

线程池 简介JUC包线程池介绍线程池最常问也最常用-参数线程执行分析-线程是怎么运行的进程和线程的区别Executors工厂类提供四种线程池Executors和ThreaPoolExecutor创建线程池的区别两种提交任务的方法spring集成的线程池 简介 线程池作为实际使用和面试较多的技能区, 学习是…...

Debian会取代CentOS成为更主流的操作系统吗?

我们知道&#xff0c;其实之前的话&#xff0c;国内用户对centos几乎是情有独钟的偏爱&#xff0c;很多人都喜欢选择centos系统&#xff0c;可能是受到一些原因的影响导致的吧&#xff0c;比如他相当于免费的红帽子系统&#xff0c;或者一些教程和网上的资料都推荐这个系统&…...

网络安全领域推荐证书介绍及备考指南

在网络安全领域&#xff0c;拥有专业认证不仅可以证明个人的专业能力&#xff0c;还能帮助在实际工作中应用先进的技术和知识。以下是几种热门的网络安全证书介绍及备考指南。 1. OSCP (Offensive Security Certified Professional) 证书简介 OSCP是针对渗透测试领域的入门级…...

SpringBoot项目ES6.8升级ES7.4.0

SpringBoot项目ES6.8.15 升级到 ES7.4.0 前言 由于公司内部资产统一整理&#xff0c;并且公司内部部署有多个版本的es集群&#xff0c;所以有必要将目前负责项目的ES集群升级到公司同一版本7.4.0。es6到es7的升级变化还是挺大的&#xff0c;因此在这里做一下简单记录&#xf…...

深度学习 之 模型部署 使用Flask和PyTorch构建图像分类Web服务

引言 随着深度学习的发展&#xff0c;图像分类已成为一项基础的技术&#xff0c;被广泛应用于各种场景之中。本文将介绍如何使用Flask框架和PyTorch库来构建一个简单的图像分类Web服务。通过这个服务&#xff0c;用户可以通过HTTP POST请求上传花朵图片&#xff0c;然后由后端…...

MFC工控项目实例二十六创建数据库

承接专栏《MFC工控项目实例二十五多媒体定时计时器》 用选取的型号为文件名建立文件夹&#xff0c;再在下面用测试的当天的时间创建文件夹&#xff0c;在这个文件中用测试的时/分/秒为数据库名创建Adcess数据库。 1、在StdAfx.h文件最下面添加代码 #import "C:/Program F…...

springmvc源码流程解析(一)

Springmvc 是基于servlet 规范来完成的一个请求响应模块&#xff0c;也是spring 中比较大的一个 模块&#xff0c;现在基本上都是零xml 配置了&#xff0c;采用的是约定大于配置的方式&#xff0c;所以我们的springmvc 也是采用这种零xml 配置的方式。 要完成这种过程&#xff…...

【论文阅读】SRGAN

学习资料 论文题目:基于生成对抗网络的照片级单幅图像超分辨率(Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network)论文地址:https://arxiv.org/abs/1609.04802代码:GitHub - xiph/daala: Modern video compression for the interne…...

kubelet PLEG实现

概述 kubelet的主要作用是确保pod状态和podspec保持一致&#xff0c;这里的pod状态包括pod中的container状态&#xff0c;个数等。 为了达到这个目的&#xff0c;kubelet需要从多个来源watch pod spec的变化&#xff0c;并周期从container runtime获取最新的container状态。比如…...

北湖区网站建设公司哪家好/泽成seo网站排名

一.我学到的内容 二.我的收获 PTA链接&#xff1a;https://pintia.cn/problem-sets?tab1 博客园链接&#xff1a;https://www.cnblogs.com/buxiu888/ 收获&#xff1a;这学期相比于上学期感觉自己收获了很多&#xff0c;不在像上学期那样去被动学习&#xff0c;因为明白了不能…...

怀化网站建设/企业网站设计服务

近期安装oracle到时候出现的一些问题记录1.安装完成无法启动,检查初始化参数2.监听配置1521以及其它端口都不通,检查是否存在多网卡,检查hosts文件,手工配置listener.ora等;3.ORA-00119: invalid specification for system parameter LOCAL_LISTENER检查hosts文件,listener.ora…...

外语网站建设目的/武汉seo网站排名优化公司

基本概念 光照影响整个场景的氛围&#xff0c;缺少光照还会缺乏三维的感觉。光照主要集中在光源的类型位置方向等参数&#xff0c;光照物体的材质(狭隘的指光照属性)和纹理&#xff0c;采用的光照模型。 OGL镜面高光颜色GL_SEPARATE_SPECULAR_COLOR在纹理处理Fragment 纹理映射…...

公司的网站如何建设/山西seo顾问

https://github.com/guaju/WYNews...

小红书推广文案/外贸网站推广优化

Example028 原文链接&#xff1a;Example028 题目 线性表 [a1, a2, a3, ..., an] 中的元素递增有序且按顺序存储于计算机内。要求设计一个算法&#xff0c;完成用最少时间在表中查找数值为 x 的元素&#xff0c;若找到&#xff0c;则将其与后继元素位置相交换&#xff0c;若找…...

沈阳网站建设建设公司哪家好/创建属于自己的网站

6月28-29日&#xff0c;中国企业云服务大会&#xff0c;在上海小南国花园酒店正式召开。联想云、阿里云等众多产业内的领先品牌就企业云服务趋势、战略、大数据技术及应用等热门议题深度交流&#xff0c;并在展览区直观呈现云技术的落地成果。联想云存储副总经理张跃华在本次盛…...