一眼就能看懂的OpenGL ES教程——缓冲区对象优化器(一)

本文为稀土金块技术社区的第一篇署名文章。 14日内禁止转载,14日后禁止擅自转载。侵权必究!

通过阅读本文,您将获得以下好处:
一、OpenGL常用缓冲对象
2. 如何使用 Vertex Buffer Objects (VBO) 优化程序

以前的评论

上一篇博文 一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(5) 绘制渐变色三角形的任务已经完成。这条路虽然很长(5篇博文),但也算是一路顺风顺水。越过前面 7 篇博文 洗礼,相信你一定能够 真实的 了解了如何使用OpenGL es绘制渐变三角形,那么恭喜你,你已经爬上了入门的第一座山。

本文不会继续爬山爬山,而是会讲一些优化的内容。当然,虽然不是一座大山,但并不代表内容简单。

image.png

OpenGL 对象

首先要谈的是OpenGL对象。这是一个什么样的物体? 官网#OpenGL对象 给出了定义:

一个 OpenGL 对象 是一个包含一些状态的 OpenGL 结构。当它们绑定到上下文时,它们包含的状态被映射到上下文的状态。因此,对上下文状态的更改将存储在此对象中,作用于此上下文状态的函数将使用存储在对象中的状态。

OpenGL 被定义为“状态机”。各种 API 调用会更改 OpenGL 状态,查询该状态的某些部分,或使 OpenGL 使用其当前状态来渲染某些内容。

对象始终是状态的容器。每种特定类型的对象都由其包含的特定状态定义。 OpenGL 对象是一种封装特定状态组并在一个函数调用中更改所有状态的方法。

提取关键点:
一眼就能看懂的OpenGL ES教程——再谈OpenGL的工作机制 我们说OpenGL是一个状态机,这里 OpenGL 对象是状态机的具体表示 .它是一个包含各种状态的结构体,它与 OpenGL 上下文交互( 一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(一) 已经提到)绑定,并设置 其自身的状态映射到上下文状态,即上下文状态一发生变化,其对应的状态就无条件地发生变化。 .

这句话你怎么看,别着急,等我提交代码后,你一定会明白的~

OpenGL 对象包含 2 个类,一个是 常规对象 ,一个是 容器对象 .

常规对象是:

容器对象是:

目前,我们不需要这么多“对象”。

d39195ed273b9167d6044d9a0ed750f7.jpeg

在下面选择 几种常见的缓冲区对象 说话。

常用缓存对象

今天要讲的对象之一叫做 缓冲对象 .每个人都应该熟悉缓冲。我们在做软件开发的时候常说在内存摘要中打开一个缓冲区,那么 这个缓冲区一般是用来临时存放一些数据的,方便以后快速获取。 .通俗地说,例如,我们将用水清洗楼梯。没有缓冲的时候,我们只好一次次去屋里舀一盆水,然后跑到楼梯上冲,但是那里有大木桶水。 ,我们只需要把水桶 大桶是缓冲器 .

那么在 OpenGL 的世界里,缓冲是以什么形式存在的呢?缓冲区在哪里?什么是缓冲来解决?

存在 一眼就能看懂的OpenGL ES教程——再谈OpenGL的工作机制 正如文章中提到的,OpenGL 图形渲染管线的执行在gpu中 , 和我们 调用指令和发送数据在cpu ,所以一定有 在 cpu 和 gpu 之间传输数据 .

首先要说的是 cpu和gpu之间的传输成本问题 ,虽然现在的cpu和gpu一般都集成在主板上,但是从传输时间上来说,它们之间的物理距离对于电子来说几乎可以忽略不计,但是当有 当非常大量的数据需要非常频繁地在cpu和gpu之间穿梭时 ,缓冲的出现势在必行。

大量数据需要非常频繁地在cpu和gpu之间穿梭的场景是大家最熟悉的。 视频渲染 .

具体的例子,比如在绘制三角形的系列中,我们每次通过 glVertexAttribPointer 该方法将顶点数据从 cpu 传输到 gpu。看起来9个浮点数就可以了,但是如果我们需要画视频, 需要渲染一帧。如果有 10000 个三角形,帧率为 30,那么一秒钟需要传输 900000 个浮点数。 ,这有点令人兴奋。

b83b71a14cd3b00ccb7b92ead9f852bd.jpeg

对于高级程序员来说,看到这种情况都是条件反射去思考如何降低传输成本,那么首先想到的就是缓冲。
所以 缓冲的基本目标是尽量减少cpu和gpu之间的传输成本,所以一般是在gpu中开辟一个缓冲区来缓冲需要的数据 .

一句话,答案就在上面 在哪里 什么问题 问题。

24ea28a2e565c4ed50853f0e57794884.jpeg

在 OpenGL 的世界中,有几个常见的缓冲区起着类似的作用,它们是 二月 ( 顶点缓冲区对象 ), 新的 顶点数组对象 , EBO (元素缓冲区对象)和 FBO ( 帧缓冲对象 )还有很多。

因为 FBO 目前 超级班 , 所以把它抛在脑后 质地 又是今天的重头戏 VBO、VAO、EBO .

缓冲对象也缓冲不同的东西。上面列出了常见的缓冲区类型。有一种类型的缓冲。 数据的专用缓冲 ,例如 VBO 专门缓冲顶点数据 , EBO 专门缓冲顶点索引数据 , 它们属于 - 缓冲对象 .还有一类 特殊缓冲状态 ,例如 新的 , 上面说了 顶点数组对象 这类。那么,我们就来详细了解一下这三者的细节和用法。

缓冲对象

我们先来看看缓冲区对象。

存在 官网Buffer对象 中的定义是:

缓冲对象 OpenGL 对象 存储由 OpenGL 上下文(又名 GPU)分配的未格式化内存数组。这些可以用来存储 顶点数据 , 从图像或帧缓冲区检索的像素数据 , 和一个 各种各样的其他东西 .

最重要的是,它是 在gpu中开辟一段数组空间来存储数据 .

这东西玩固定套路:

创建缓冲区

绑定缓冲区到上下文

将顶点数据存入缓冲区中

将指定如何解析顶点属性数组

绘制

解绑缓冲区

删除缓冲区

体现在代码中的是:

glGenBuffers

glBindBuffer

glBufferData

glVertexAttribPointer

glDrawArrays

解绑的glBindBuffer

glDeleteBuffers

关于代码,主要关注3点:
1 .创建时通过 指定glGenBuffers的type参数来确定Buffer Objects的具体类型 ,最常见的有以下几种:

GL_ARRAY_BUFFER , GL_ELEMENT_ARRAY_BUFFER , GL_SHADER_STORAGE_BUFFER , GL_PIXEL_PACK_BUFFER 等等,在哪里 GL_ARRAY_BUFFER 对应 VBO , GL_ELEMENT_ARRAY_BUFFER 对应 EBO .

2 。经过 glBindBuffer 将当前缓冲区对象绑定到上下文 .这里涉及的是OpenGL状态机的概念。最重要的是要知道,上面已经提到, 一旦一个缓冲区对象和上下文被绑定,所有后续操作都在缓冲区对象上执行,直到它被解除绑定 ,

3 。经过 glBufferData 将数据存储在缓冲区中。关于这个功能,还需要说几句。

该函数声明为 ( 官方网站 ):

 void glBufferData(GLenum 目标,GLsizeiptr 大小,const void * 数据,GLenum 使用);  
 复制代码

目标 表示对应的具体Buffer Object类型,即上面列出的GL_ARRAY_BUFFER和GL_ELEMENT_ARRAY_BUFFER。

尺寸 表示传入的数据长度。

数据 它是指向特定数据的指针。

用法 指定数据的访问模式。模式的可选值是 GL_STREAM_DRAW , GL_STREAM_READ , GL_STREAM_COPY , GL_STATIC_DRAW , GL_STATIC_READ , GL_STATIC_COPY , GL_DYNAMIC_DRAW , GL_DYNAMIC_READ , 或者 GL_DYNAMIC_COPY .

最常用的是指定 修改频率模式 ,也就是告诉OpenGL我们多久修改一次数据,目的是为了让 OpenGL决定如何存储这些数据,因为不同修改频率的数据会被存储在gpu的不同区域以尽量获得最佳性能 .

访问频率模式如下:

  • 溪流 :数据几乎每次被访问时都会被修改。
  • 静止的 : 数据只修改一次。
  • 动态的 : 数据会被多次修改。

52cac471e20453d76ea471a5dfc406ca.jpeg

二月

和上面的例子一样,如果一帧需要渲染 10000 个三角形,帧率为 30,那么一秒需要传输 900000 个浮点数。然后根据上面对缓冲对象的解释, 是否可以在 gpu 中缓存顶点数据? ?如果渲染出来的图形是不动的(其实顶点数据即使需要改变也是可以固定的),那么事实上 只需将顶点数据传输到 gpu 一次 ,即可以在第一帧传递顶点数据。如果每帧绘制 10,000 个三角形,则 只需在第一帧中传递 30,000 个顶点坐标 .

所以 二月 ,马上 顶点缓冲区对象 顾名思义,就是为解决这种情况而生的。

使用 VBO 的代码:

 静态浮点三角形VerWithColor[] = {  
 0.0f, 0.8f, 0.0f, //顶点  
 -0.8f, -0.8f, 0.0f, //顶点  
 0.8f, -0.8f, 0.0f, //顶点  
 };  
  
 //定义vbo的id数组,因为可能需要创建多个vbo  
 无符号整数 VBO[1];  
 //创建一个vbo并将创建的vbo的id存入VBOs数组  
 glGenBuffers(1,VBO);  
 //此时context绑定了VBOs[0]对应的vbo缓冲区  
 glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);  
 //将顶点数据存入vbo缓冲区  
 glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVerWithColor), triangleVerWithColor, GL_STATIC_DRAW);  
 //指定如何解析顶点属性数组,注意这里最后一个参数不是原始数组的地址,而是vbo缓冲区中数据的相对地址  
 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 二十四, (void*) 0);  
 //在着色器中打开布局为0的输入变量  
 glEnableVertexAttribArray(0);  
    
 //清屏  
 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);  
 glClear(GL_COLOR_BUFFER_BIT);  
 // 绘制三角形  
 glDrawArrays(GL_TRIANGLES, 0, 3);  
 //窗口显示,交换双缓冲  
 eglSwapBuffers(显示,winSurface);  
 //解除绑定VBO  
 glBindBuffer(GL_ARRAY_BUFFER, 0);  
 //删除VBO,即清除缓冲区  
 glDeleteBuffers(1, VBO);  
 // 释放着色器程序对象  
 glDeleteProgram(程序);  
  
 复制代码

很多人在第一次看到类似glBindXX的代码时会感到困惑。对此,他们主要需要了解OpenGL状态机的概念。详细信息已经在( 一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(一) 如前所述,一般来说,当调用 glBindXX 指令时, OpenGL状态机进入某个状态 , 直到调用对应的解绑方法。之间的所有通话都是 对于这个状态 (好像说过很多次了……)。

那么在VBO的使用中,可以理解为执行
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
之后,下面的glBufferData和glVertexAttribPointer,包括绘图,都是 基于VBO操作 ,所以请注意 glVertexAttribPointer 传递给该方法的最后一个参数是 (无效*)0 ,而不是之前的数组。

这里可以这样理解:

曾是
cpu 告诉 gpu:“老兄,你可以使用物理内存中地址为 xx 的 xx 数组”

这是
cpu首先将数组数据传输到gpu中的vbo缓冲区,然后调用 glVertexAttribPointer 使用方法的时候,我对gpu说:“哥们,你vbo里面的xx地址的数据怎么用……”

顶点着色器:

 #版本 300 是  
 布局(位置 = 0)  
 //输入顶点坐标将在程序中指定,将数据输入到该字段中。如果传入的向量不够4维,前三个分量会自动设置为0.0,最后一个分量会设置为1.0  
 在 vec4 位置;  
  
 无效的主要(){  
 // 直接使用传入的坐标值作为传入的渲染管线。 gl_Position 内置于 OpenGL  
 gl_Position = aPosition;  
            
 };  
 复制代码

片段着色器:

 #版本 300 是  
 精度 mediump 浮点数;  
        
 出 vec4 FragColor;  
  
 无效的主要(){  
 //gl_FragColor 内置于 OpenGL  
 FragColor = vec4(1.0, 0.0, 0.0, 1.0);  
 };  
 复制代码

那么如果有多个顶点属性呢?例如,添加颜色属性。

代码只需要微调:

顶点属性数组改为:

 静态浮点三角形VerWithColor [] = {  
 0.0f, 0.8f, 0.0f, //顶点  
 1.0, 0.0, 0.0, //颜色  
 - 0.8f, - 0.8f, 0.0f, //顶点  
 0.0, 1.0, 0.0, //颜色  
 0.8f, - 0.8f, 0.0f, //顶点  
 0.0, 0.0, 1.0, //颜色  
 };  
 复制代码

解析数据逻辑添加2行解析第二个属性:

 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 二十四, (void*) 0);  
 //关键是最后一个参数传递地址偏移量  
 glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 二十四, (void*)( 3* 4));  
  
 glEnableVertexAttribArray(0);  
 glEnableVertexAttribArray(1);  
 复制代码

这里要注意的一点是,在解析第二个属性的时候, 传递数组中第二个属性地址对应的指针 , 实际上 它在 VBO 中的相对地址 ,因为第二个属性从第4个元素开始,即使偏移了3个float元素,所以地址指针为 (无效*)(3*4)

顶点着色器:

 #版本 300 是  
 布局(位置 = 0)  
 //输入顶点坐标将在程序中指定,将数据输入到该字段中。如果传入的向量不够4维,前三个分量会自动设置为0.0,最后一个分量会设置为1.0  
 在 vec4 位置;  
  
 //输入顶点的颜色 //如果传入的向量不够4维,自动设置前三个分量为0.0,最后一个分量为1.0  
 布局(位置 = 1)  
 在 vec4 aColor;  
 // 输出颜色  
 出 vec4 vTextColor;  
  
 无效的主要(){  
 // 直接使用传入的坐标值作为传入的渲染管线。 gl_Position 内置于 OpenGL  
 gl_Position = aPosition;  
 vTextColor = aColor;  
 };  
 复制代码

片段着色器:

 #版本 300 是  
 精确  
 mediump 浮动;  
 // 输入颜色  
 在 vec4 vTextColor;  
 出 vec4 FragColor;  
  
 无效的主要(){  
 //gl_FragColor 内置于 OpenGL  
 FragColor = vTextColor;  
 };  
 复制代码

在正常使用中,其实只需要配置一次VBO,并且可以多次调用它来绘制VBO中的顶点数据,那么就可以 配置和绘图逻辑分离

配置代码:

 无符号整数 VBO [1];  
  
 glGenBuffers(1,VBO);  
  
 glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);  
 glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVerWithColor), triangleVerWithColor, GL_STATIC_DRAW);  
  
 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 二十四, (void*) 0);  
 glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 二十四, (void*)( 3* 4));  
  
 glEnableVertexAttribArray(0);  
 glEnableVertexAttribArray(1);  
 //数据传输后解除绑定  
 glBindBuffer(GL_ARRAY_BUFFER, 0);  
 复制代码

绘图代码:

 //绘制前记得绑定VBO  
 glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);  
 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);  
 glClear(GL_COLOR_BUFFER_BIT);  
  
 glDrawArrays(GL_TRIANGLES, 0, 3);  
 //窗口显示,交换双缓冲  
 eglSwapBuffers(显示,winSurface);  
 //绘制完成并解除绑定VBO  
 glBindBuffer(GL_ARRAY_BUFFER, 0);  
 复制代码

想象一下播放一段视频,后面的绘制代码每秒绘制几十次,所以你可以看到 VBO 是如何优化性能的,因为 只需要配置一次数据,即cpu向gpu传输一次数据,以后每次绘制都会直接取gpu中缓存的顶点数据。 .

运行之后,又是熟悉的老朋友了。 只不过这次draw的是从gpu中的一个buffer中取出的顶点数据 .

image.png

总结

本文主要介绍OpenGL对象和其中的缓冲区对象,重点介绍VBO的功能和用法。由于篇幅有限,剩下的缓冲对象将在下一篇文章中介绍。感谢您的支持和关注。

a20241bc1722029d6929b0dd299e1731.jpeg

代码地址

(项目代码会不断更新)
github.com/yishuinanfe…

参考

OpenGL 对象
缓冲对象
如何理解OpenGL的VAO和VBO
熟悉OpenGL VAO、VBO、FBO、PBO等对象,读完这篇文章就够了

原创不易。如果你觉得这篇文章对你有帮助,别忘了点赞和关注。这也是我创作的最大动力~

系列文章目录

有关更多博客文章,请参阅 浪漫马车影音系统学习总目录

实际项目: 介绍一个刚刚发布的Android音视频播放录制开源项目

C/C++基础与进阶之路

音视频理论基础系列专栏

音视频开发实战系列专栏

易上手OpenGL系列
一个一眼就能看懂的OpenGL ES教程——关于图形渲染管线的那些事儿
一眼就能看懂的OpenGL ES教程——再谈OpenGL的工作机制
一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(一)
一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(二)
一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(三)
一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(4)
一个一眼就能看懂的OpenGL ES教程——这可能是你遇到过的最难的三角形(5)
一眼就能看懂的OpenGL ES教程——缓冲区对象优化器(一)

版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议。转载请附上原文出处链接和本声明。

这篇文章的链接: https://homecpp.art/0625/10631/1125

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明

本文链接:https://www.qanswer.top/39286/58022512

posted @ 2022-09-25 12:58  哈哈哈来了啊啊啊  阅读(250)  评论(0编辑  收藏  举报