1.2 顶点数组对象
使用了顶点缓冲技术后,绘制效率有了较大的提升。但是还有一点不尽如人意,那就是顶点的位置坐标、法向量、纹理坐标等不同方面的数据每次使用时需要单独指定,重复了一些不必要的工作。OpenGL ES 3.0考虑到了这一点,提供了一种专门用于解决此问题的对象——顶点数组对象(VAO)。本节将介绍顶点数组对象。
1.2.1 基本知识与案例效果
顶点数组对象的主要功能就是将绘制一个物体时所需的对应于不同方面(如顶点坐标、法向量、纹理坐标)的顶点缓冲及相关设置包装成一个整体,绘制时直接使用顶点数组对象即可,不必分别使用每个顶点缓冲,这可以进一步提高绘制效率。
了解了顶点数组对象的主要功能后,下面介绍两个与使用顶点数组对象相关的方法(这两个方法也是通过GLES30类进行调用),具体内容如下。
- glGenVertexArrays方法
glGenVertexArrays方法主要用于创建新的顶点数组对象,该方法的具体签名如下。
1 public static void glGenVertexArrays (int n, int[] arrays, int offset)
说明
参数n为需要创建的顶点数组对象的数量,参数arrays用来存放创建的n个顶点数组对象的编号,参数offset为arrays数组的偏移量。
- glBindVertexArray方法
创建顶点数组对象之后,就可以用glBindVertexArray方法绑定指定的顶点数组对象,以便对指定的顶点数组对象进行设置或使用,其方法签名如下。
1 public static void glBindVertexArray (int array)
说明
参数array为需要绑定的顶点数组对象的编号。用glBindVertexArray方法绑定顶点数组对象之后,更改顶点数组状态的后续调用(如glBindBuffer、glVertexAttribPointer、glEnableVertexAttribArray和glDisableVertexAttribArray等)将影响绑定的顶点数组对象。通过绑定不同的顶点数组对象,绘制时应用程序可以快速在不同顶点数组配置之间进行切换(只需一句代码即可),大大提高了开发效率。
了解了顶点数组对象的一些基本知识以后,下面来了解一下本节案例Sample1_2的运行效果,具体情况如图1-2所示。
提示
运行本案例时,当手指在屏幕上上下左右滑动时,相应的物体也会绕x轴和y轴旋转。
1.2.2 案例开发步骤
了解了顶点数组对象的基本知识以及本节案例的运行效果后,就可以进行案例的开发了。由于案例Sample1_2是在案例Sample1_1的基础上修改而来的,所以这里仅给出相关的修改部分,具体步骤如下。
(1)首先介绍的是用于初始化顶点数组对象的方法initVAO,此方法用于在绘制之前一次性对顶点数组对象进行初始化,其具体代码如下。
1 public void initVAO(){
2 int[] vaoIds=new int[1]; //用于存放顶点数组对象编号的数组
3 GLES30.glGenVertexArrays(1, vaoIds, 0); //创建一个顶点数组对象
4 vaoId=vaoIds[0]; //获取将创建的顶点数组对象编号
5 GLES30.glBindVertexArray(vaoId); //绑定顶点数组对象
6 GLES30.glEnableVertexAttribArray(maPositionHandle); //启用顶点位置数据数组
7 GLES30.glEnableVertexAttribArray(maNormalHandle); //启用法向量数据数组
8 GLES30.glEnableVertexAttribArray(maTexCoorHandle); //启用纹理坐标数据数组
9 //绑定到顶点坐标数据缓冲
10 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mVertexBufferId);
11 //指定顶点位置数据使用对应缓冲
12 GLES30.glVertexAttribPointer(maPositionHandle, 3, GLES30.GL_FLOAT,false,3*4,0);
13 //绑定到顶点法向量数据缓冲
14 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mNormalBufferId);
15 //指定顶点法向量数据使用对应缓冲
16 GLES30.glVertexAttribPointer (maNormalHandle, 3,GLES30.GL_FLOAT, false,3*4,0);
17 //绑定到顶点纹理坐标数据缓冲
18 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mTexCoorBufferId);
19 //指定顶点纹理坐标数据使用对应缓冲
20 GLES30.glVertexAttribPointer(maTexCoorHandle,2,GLES30.GL_FLOAT,false,2*4,0);
21 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0); //绑定到系统默认缓冲
22 GLES30.glBindVertexArray(0); //绑定到系统默认顶点数组对象
23 }
提示
上述initVAO方法主要用于创建并初始化顶点数组对象,包括生成VAO,绑定VAO,启用顶点位置、法向量、纹理坐标数据数组,然后依次将顶点坐标数据、顶点法向量数据以及顶点纹理坐标数据指定到相应的缓冲,最后绑定到系统默认的缓冲以及系统默认的顶点数组对象,此方法在初始化顶点数据时调用一次。
(2)接下来将要介绍的是用于绘制物体的方法drawSelf,该方法与案例Sample1_1中对应的方法有所不同。此处的绘制方法功能更为简单,省略了在绘制时一一绑定所需的顶点缓冲,并为不同数据(顶点坐标、法向量、纹理坐标)一一指定缓冲的代码,其具体代码如下。
1 public void drawSelf(int texId){
2 GLES30.glUseProgram(mProgram); //指定使用某套着色器程序
3 //将最终变换矩阵传入渲染管线
4 GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0);
5 //将基本变换矩阵传入渲染管线
6 GLES30.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0);
7 //将光源位置传入渲染管线
8 GLES30.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB);
9 //将摄像机位置传入渲染管线
10 GLES30.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB);
11 GLES30.glBindVertexArray(vaoId); //绑定顶点数组对象
12 GLES30.glActiveTexture(GLES30.GL_TEXTURE0); //激活纹理
13 GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId); //绑定纹理
14 GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vCount); //绘制加载的物体
15 GLES30.glBindVertexArray(0); //绑定到系统默认的顶点数组对象
16 }
说明
该绘制方法的功能主要为指定使用某套着色器程序,将最终变换矩阵、基本变换矩阵、光源位置及摄像机位置等数据送入渲染管线,并绑定顶点数组对象,激活和绑定纹理,绘制物体之后绑定到系统默认的顶点数组对象。从与案例Sample1_1中的drawSelf方法进行对比可以看出,其代码更短,开发成本更低,读者在实际开发中应该尽量选用。