天天看点

《OpenGL ES 3.x游戏开发(下卷)》一1.4 映射缓冲区对象

本节书摘来异步社区《opengl es 3.x游戏开发(下卷)》一书中的第1章,第1.4节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区“异步社区”公众号查看。

前面几节已经介绍了顶点缓冲区对象、顶点数组对象以及一致缓冲区对象,通过使用这些技术可以在很大程度上提高绘制效率。本节将介绍在某些情况下可以进一步提高效率的映射缓冲区对象(mapping buffer objects)。

本章前面的几个案例中,都是通过调用glbufferdata方法或者glbuffersubdata方法向缓冲区中送入数据或者更新数据的。采用这种策略时需要首先将数据在内存中准备好,然后再通过glbufferdata方法或glbuffersubdata方法将数据从内存复制到显存中。

这对于缓冲中数据不变或变化率很低的情况基本够用了,但是对于绘制过程中频繁变化的数据就显得效率不够高。本小节将介绍一种针对此问题的解决方案——映射缓冲区对象。通过使用映射缓冲区对象,可以在绘制过程中数据频繁变化的情况下进一步减少内存消耗并提高渲染效率。

提示 所谓映射缓冲区对象就是将显存中的存储映射到虚拟的内存地址上,使得开发人员可以使用如同访问内存一样的api访问显存以提高效率。

使用映射缓冲区对象主要涉及的方法有3个,具体内容如下。

glmapbufferrange方法

glmapbufferrange方法用于将指定缓冲对应的显存映射到虚拟的内存地址上,并返回映射的结果,以便开发人员使用它来更新显存中的数据。如果出现错误或者发出无效请求,该方法将返回空,其具体方法签名如下。

说明 参数target用于描述需映射的缓冲区类型,可以设置的值如本章前面表1-1所列;参数offset为被映射的缓冲区数据存储中的偏移量;参数length为需要映射的缓冲区数据字节数;参数access为访问标志,可选的访问标志如表1-6所列。
《OpenGL ES 3.x游戏开发(下卷)》一1.4 映射缓冲区对象
表1-6中的访问标志在不冲突的情况下可以同时使用多个标志,使用多个标志时用“|”隔开。另外,实际开发中一般至少选用gl_map_read_bit与gl_map_write_bit中的一个,而其他选项则进一步根据需要选择即可。

glunmapbuffer方法

glunmapbuffer方法用于解除缓冲区映射,其具体方法签名如下。

1 public static boolean glunmapbuffer (int target)

参数target用于描述需解除映射的缓冲区类型,可以设置的值如本章前面表1-1所列。如果解除映射操作成功,则返回true,并且前面glmapbufferrange方法返回的映射范围在取消映射操作成功之后不再可用。如果顶点缓冲区对象数据存储中的数据在缓冲区映射之后已经破坏,则glunmapbuffer方法返回false。

glflushmappedbufferrange方法

glflushmappedbufferrange方法用于通知渲染管线被映射缓冲区中的数据已经被修改,类似于i/o操作时用于刷新数据的flush方法,其具体方法签名如下。

参数target用于描述需刷新数据所属的被映射缓冲区类型;参数offset为被映射的缓冲区数据存储中的偏移量;参数length为需要刷新的被映射缓冲区数据字节数。需要注意的是,glflushmappedbufferrange方法所操作的缓冲必须用glmapbufferrange方法在映射时选用了gl_map_flush_explicit_bit选项。

了解了映射缓冲区对象的基本知识以后,就可以进行案例的开发了。在开发案例之前,首先应该了解本节案例sample1_4的运行效果,具体情况如图1-4所示。

《OpenGL ES 3.x游戏开发(下卷)》一1.4 映射缓冲区对象
《OpenGL ES 3.x游戏开发(下卷)》一1.4 映射缓冲区对象
从图1-4中可以看出,运行过程中球体的上半部分在球体与立方体之间连续变换着。这是由于运行过程中程序不断进行缓冲区映射,并连续更新球体上半部分的顶点数据。

了解了映射缓冲区对象的基本知识与案例效果后,就可以进行代码的开发了。由于本案例中的很多类与前面案例中的很相似,因此这里仅给出本案例中具有特殊性及代表性的代码,具体内容如下。

(1)首先介绍的是ballandcube类中用于初始化顶点数据的initvertexdata方法,在该方法中向顶点坐标数据缓冲中送入数据时就采用了映射缓冲区,具体内容如下。

上述代码中最有代表性的就是第8~第21行,其中没有使用传统的glbufferdata方法向顶点坐标数据缓冲中送入数据,而是使用glmapbufferrange方法先进行缓冲映射,然后再直接将数据送入映射后的缓冲中。需要注意的是,每次映射并更新数据完毕后,都需要使用glunmapbuffer方法解除映射,否则渲染管线在绘制时,无法正常使用被映射缓冲区中的数据。

(2)了解了用于初始化顶点数据的initvertexdata方法后,接下来要介绍的是用于在运行过程中连续计算顶点坐标的方法calvertices和插值方法insertvalue,具体代码如下。

第1~第7行为在运行过程中不断被调用以计算当前顶点坐标位置为从球到立方体或从立方体到球变化服务的calvertices方法。此方法每次被调用时遍历每个顶点坐标,根据count参数以及flag参数值调用insertvalue方法完成插值计算。需要特别注意的是第5~第7行,每次计算完成后,将数据复制进绘制用数组时都需要加锁,以避免复制的同时进行绘制造成画面撕裂的问题。

第8~第15行为在球与立方体之间进行顶点坐标插值计算的insertvalue方法,从代码中可以看出此方法采用的是线性插值。

(3)接下来详细介绍用于更新顶点数据到映射缓冲区的方法updatemapping,其功能为根据接收到的顶点坐标数据更新顶点缓冲区对象中的数据,具体代码如下。

上述方法在运行中定时被调用,用于将计算出来的新的顶点坐标数据更新到对应的缓冲中,供渲染管线在绘制时使用,主要套路与前面initvertexdata方法中的对应部分相同。

(4)了解了在绘制过程中不断被调用以便计算与更新顶点坐标的相关方法后,下面来了解一下定时执行ballandcube类中calvertices方法的线程类——updatethread,其具体代码如下。

上述updatethread类非常简单,主要是在其run方法中定时调用calvertices方法更新顶点坐标数据。同时每次更新后会检查是否达到一轮变化所需的总步骤数,若达到了则将步骤计数器置0,并将变化方向标志位isballcube(用于表示变化方向是从球到立方体还是立方体到球)置返。

(5)上一步介绍了定时执行ballandcube类中calvertices方法的线程类——updatethread,接下来介绍ballandcube类中的绘制方法drawself,其具体代码如下。

第2~第4行设置物体绕x、y、z轴旋转指定的角度。

第5~第18行首先指定使用某套着色器程序,然后将最终变换矩阵送入渲染管线,接着绑定了顶点位置、纹理坐标缓冲,并指定顶点位置、纹理坐标使用对应的缓冲,同时还启用了顶点坐标以及纹理坐标数据数组。

第19~第23行激活并绑定了指定的纹理,然后同步加锁后调用updatemapping方法更新顶点数据。这里的同步加锁与前面calvertices方法中是对应的,目的是为了避免更新缓冲中数据的同时数据数组curballfordraw被其他线程访问。

第24~第28行首先绑定索引缓冲对象,并以三角形方式执行绘制,最后绑定到系统默认的索引与数组缓冲。

继续阅读