一个专注音视频领域问答的小圈子

在之前使用 OpenGL 顶点缓冲区 VBO 的使用 为顶点坐标、纹理坐标分别绑定了顶点缓冲区,并且在 onDrawFrame 方法里面也要分别为顶点坐标、纹理坐标指定数据。

这就存在了一些重复的操作。

在 OpenGL ES 3.0 可以使用顶点数组对象来解决这一问题。

顶点数组对象又叫做 VAO (Vertex Array Object)。

顶点数组对象的主要功能就是将绘制的一个物体时所需的对应于不同方面的顶点缓冲及相关设置包装成一个整体,在绘制时直接使用顶点数组对象即可,不必分别使用每个顶点缓冲,这样可以进一步提高绘制效率。

方法介绍

顶点数组对象主要提供了 glGenVertexArrays 和 glBindVertexArray 方法。

glGenVertexArrays 方法

glGenVertexArrays 方法主要用于创建新的顶点数组对象,方法签名如下:

    public static native void glGenVertexArrays(
        int n,
        int[] arrays,
        int offset
    );

其中,参数 n 为要创建的顶点数组对象的数量,参数 arrays 用来存放创建的 n 个顶点数组对象的编号,参数 offset 为 arrays 数组的偏移量。

glBindVertexArray 方法

创建顶点数组对象之后,就可以用 glBindVertexArray 方法绑定指定的顶点数组对象,以便对指定的顶点数组对象进行设置或使用,方法签名如上:

    public static native void glBindVertexArray(
        int array
    );

其中,参数 array 为需要绑定的顶点数组对象的编号。

用 glBindVertexArray 方法绑定顶点数组对象之后,更改顶点数组状态的后续调用(如 glBindBuffer、glVertexAttribPointer、glEnableVertexAttribArray、glDisableVertexAttribArray 等)将影响绑定的顶点数组对象。

通过绑定不同的顶点数组对象,绘制时应用程序可以快速在不同顶点数组配置之间进行切换,大大提高了开发效率。

实践

下面是具体的实践,还是绘制如下的一张纹理图:

顶点数组对象的使用

使用顶点数组对象 VA0 之前,还是分别为顶点坐标、纹理坐标生成缓冲区,并传入数据。

		// 生成纹理缓冲区
        int[] bufferIds = new int[3];
        GLES30.glGenBuffers(3, bufferIds, 0);
        // 顶点缓冲
        mVertexBufferId = bufferIds[0];
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVertexBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertices.length * 4, mVertexBuffer, GLES30.GL_STATIC_DRAW);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
        // 纹理缓冲
        mTextureBufferId = bufferIds[1];
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mTextureBufferId);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, textures.length * 4, mTextureBuffer, GLES30.GL_STATIC_DRAW);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);

接下来就是使用顶点数组对象了。

		// 生成顶点数组对象
        int[] vaoIds = new int[1];
        GLES30.glGenVertexArrays(1, vaoIds, 0);
        mVAOId = vaoIds[0];
        // 绑定顶点数组对象
        GLES30.glBindVertexArray(mVAOId);
		// 在绑定顶点数组对象之后 调用 glBindBuffer glVertexAttribPointer
		// glEnableVertexAttribArray,glDisableVertexAttribArray 等等
		// 将影响绑定的顶点数组对象
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVertexBufferId);
        GLES30.glEnableVertexAttribArray(maPositionHandle);
        GLES30.glVertexAttribPointer(maPositionHandle, 3, GLES30.GL_FLOAT, false, 3 * 4, 0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);

        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mTextureBufferId);
        GLES30.glEnableVertexAttribArray(maTexCoorHandle);
        GLES30.glVertexAttribPointer(maTexCoorHandle, 2, GLES30.GL_FLOAT, false, 2 * 4, 0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
		// 绑定到默认的顶点数组对象
		  GLES30.glBindVertexArray(0);

这里要注意的是,调用了 glBindVertexArray 绑定顶点数组对象之后,我们调用的 glBindBuffer、glVertexAttribPointer、glEnableVertexAttribArray、glDisableVertexAttribArray 等方法都属于该顶点数组对象之内了,都会影响到这个顶点数组对象,直到绑定了默认的顶点数组对象。

在我们使用 顶点缓冲区 VBO 绘制时,要在 onDrawFrame 方法里面为每一个顶点坐标、纹理坐标调用 glBindBuffer 、glVertexAttribPointer、glEnableVertexAttribArray 方法,而使用了 顶点数组对象 VA0 之后,就不需要这么麻烦了,因为相当于顶点数组对象把它们都接管了,直接绑定顶点数组对象就好了。

使用 顶点数组对象 VAO 绘制的代码如下:

		// 绑定顶点数组对象
        GLES30.glBindVertexArray(mVAOId);
        GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId);
        GLES30.glDrawElements(GLES30.GL_TRIANGLES, vCount, GLES30.GL_UNSIGNED_BYTE, 0);
        GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);
		// 解除绑定顶点数组对象
        GLES30.glBindVertexArray(0);

通过这种方式相当于替代了那些繁琐的方法,并且可以直接通过更改绑定的顶点数组对象 ID 去更改绘制的内容,简单高效地实现切换顶点数据。

关于具体的代码实现,可以参考我的 Github 项目:

https://github.com/glumes/AndroidOpenGLTutorial

参考

  1. 《OpenGL ES 3.x 游戏开发》

知识星球

一个专注音视频领域问答的小圈子

公众号音视频开发进阶对应的知识星球,一个编程开发领域的专业圈子,贩卖知识和技巧!

※ 入群须知:了解该星球能提供的价值和帮助,在提问时务必阐述好背景,附带相关的信息。

iOS 用户可以加我微信 ezglumes 邀请你进星球,有疑问也可以加我微信咨询。

※ 星球内容:

基础教程:

在知识星球连载的干货教程,可以在专栏中找到,随着时间的推移,教程也会越来越多:

- 音视频基础概念
- WebRTC 入门教程及源码实践
- 播放器教程及源码实践
- OpenGL 和特效开发教程
- Vulkan 入门教程

部分内容可以在博客 https://glumes.com 中检索到,后面会在星球里持续更新.

干货分享:

涵盖了移动开发和音视频工程领域的绝大部分,从项目实战角度出发,提升能力,包括但不限于以下领域:

- Android/iOS 移动开发
- Camera 开发
- 短视频编辑 SDK 项目实践
- 在线直播和推流
- WebRTC 开发
- 播放器基础和提高
- OpenGL 图像渲染及特效开发
- C++ 基础和提高
- FFmpeg 使用和分析
- 干货资源和书籍分享

不止于技术方面的,各种 IT 新闻、茶余饭后、生活趣事也欢迎大家分享!!!

技术答疑解惑:

针对上述基础教程和干货分享的答疑,另外还有音视频和 IT 开发中的各种交流讨论。

- 基础知识点答疑
- 工业项目实践答疑
- 问题排查思路分析

一个 BUG 排查很久,不如来星球里提个问题,效率提升百倍。

求职和面试辅导:

一站式职场服务,每份工作都值得用心对待!!!

- 面试题和面试经验分享
- 简历修改和模拟面试
- 大厂内推和信息同步
- 职场经验分享
- 职业规划和发展分析

※ 星主和合伙人介绍

星主是公众号音视频开发进阶的作者,也是网站 https://glumes.com 的作者,曾参与过抖音、剪映等头部音视频 APP 底层 SDK 的开发。

合伙人也是在头条、快手从事音视频架构师的职位,具有多年的音视频开发经验,能力圈覆盖了音视频的绝大多数领域,资深音视频从业人员为你保驾护航。

微信公众号

扫描下面的二维码关注我的微信公众号《音视频开发进阶》,推送更多精彩内容!

添加我的微信 ezglumes 拉你入音视频与图形图像技术群一起交流学习~

wechat-account-qrcode

原创文章,转载请注明来源:    《OpenGL ES 3.x 游戏开发》 顶点数组对象 VAO 的使用