《OpenGL ES 3.x游戏开发(下卷)》一1.1 顶点缓冲区

1.1 顶点缓冲区

将绘制物体的顶点数据保存在内存中,在调用glDrawArrays或者glDrawElements等绘制方法前需要调用相应的方法将数据送入显存,I/O开销大,性能不够好。

若采用顶点缓冲区对象存放顶点数据,则不需要在每次绘制前都将顶点数据复制进显存,而是在初始化顶点缓冲区对象时一次性将顶点数据送入显存,每次绘制时直接使用显存中的数据,可以大大提高渲染性能。

1.1.1 基本知识

OpenGL ES 3.0中支持两种类型的顶点缓冲区对象,分别为数组缓冲区对象(Array Buffer)和元素数组缓冲区对象(Element Array Buffer),具体情况如下:

数组缓冲区对象对应的类型为GL_ARRAY_BUFFER,一般用于存放待绘制物体的顶点相关数据,如顶点坐标、纹理坐标、法向量等。
元素数组缓冲区对象对应的类型为GL_ELEMENT_ARRAY_BUFFER,一般用于存放图元的组装索引数据,在使用索引法进行绘制时使用。
了解了两种类型的顶点缓冲区对象后,下面接着介绍缓冲区对象的一些常用操作方法。主要包括创建缓冲区的方法glGenBuffers、绑定缓冲区的方法glBindBuffer、向缓冲区送入数据的方法glBufferData和glBufferSubData、删除缓冲区的方法glDeleteBuffers以及查询指定缓冲区相关信息的方法glGetBufferParameteriv等,这些方法都可以通过GLES30类调用。

  • glGenBuffers方法

glGenBuffers方法用于创建缓冲对象,是在使用每个自定义缓冲前都需要调用的,其方法签名如下。

1    public static void glGenBuffers (int n, IntBuffer buffers)
2    public static void glGenBuffers (int n, int[] buffers, int offset)

第一个用于创建缓冲对象的glGenBuffers方法共有2个参数,其中参数n为需要创建的缓冲区数量,参数buffers为用于存放创建的n个缓冲区编号的IntBuffer。
第二个用于创建缓冲对象的glGenBuffers方法共有3个参数,其中参数n为需要创建的缓冲区数量,参数buffers为用于存放创建的n个缓冲区编号的数组,参数offset为buffers数组所需的偏移量。
 说明

请读者注意,通过此方法获得的缓冲区编号是0以外的无符号整数,0号缓冲OpenGL ES内部保留使用,不用于自定义缓冲。

  • glBindBuffer方法

glBindBuffer方法用于绑定当前缓冲区对象,第一次调用glBindBuffer方法绑定缓冲区对象时,缓冲区对象以默认状态分配;如果分配成功,则分配的对象绑定为当前缓冲区对象。该方法签名如下。

1    public static void glBindBuffer (int target, int buffer)

说明

参数target用于描述需绑定的缓冲区类型,其可能的取值如表1-1所列;参数buffer为需要绑定的缓冲区的编号。

提示

表1-1中的target参数值,不仅仅用于glBindBuffer方法中,接下来将要介绍的glBufferData和glBufferSubData方法中都将用到,请读者注意。

  • glBufferData方法

glBufferData方法一般用于向指定缓冲中送入数据,也可以用于对指定缓冲进行相关的存储空间初始化,其签名方法如下。

1    public static void glBufferData (int target, int size, Buffer data, int usage)

说明

参数target用于描述指定的缓冲区类型(如表1-1所列);参数size用于给出缓冲区的大小(单位为字节);参数data为需要送入缓冲的数据,若没有数据要送入缓冲区,其值可以为null;参数usage用于指定缓冲区的用途,其可能的取值如表1-2所列。

提示

请读者注意,usage参数仅仅用于辅助性描述指定缓冲的用途,在有些情况下可以帮助渲染管线优化操作,并不是强制性的。如在应用程序中将缓冲区用途设置为GL_STATIC_DRAW,但还是可以对缓冲数据进行多次修改。一般来说,设置的用途如果和实际使用匹配的话将更有利于渲染管线工作,因此并不建议读者随便设置。

  • glBufferSubData方法

glBufferSubData方法一般用于向指定缓冲中送入部分数据进行初始化或者更新,其方法签名如下。

1    public static void glBufferSubData (int target, int offset, int size, Buffer data)

说明

参数target用于描述指定的缓冲区类型(如表1-1所列);参数offset用于给出缓冲区被修改的数据的起始内存偏移量;参数size用于给出缓冲区中数据被修改的字节数;参数data为需要送入缓冲的数据。

  • glDeleteBuffers方法

glDeleteBuffers方法用于删除指定的缓冲区对象,其方法签名如下。

1    public static void glDeleteBuffers (int n, IntBuffer buffers)
2    public static void glDeleteBuffers (int n, int[] buffers, int offset)

第一个用于删除指定缓冲区对象的glDeleteBuffers方法共有两个参数,其中参数n为将要被删除的缓冲区对象数量,参数buffers为存储了n个要删除的缓冲区编号的IntBuffer。
第二个用于删除指定的缓冲区对象的glDeleteBuffers方法共有3个参数,其中参数n为将要被删除的缓冲区对象数量,参数buffers为存储了n个要删除缓冲区编号的数组,参数offset为数组所需的偏移量。

说明

在实际开发中,不再需要的缓冲区应该尽早用glDeleteBuffers方法删除,以便及时释放资源,提高系统的运行效率。

  • glGetBufferParameteriv方法

前面介绍了缓冲区对象的创建、绑定以及初始化等相关方法,在运行中还可以调用glGetBufferParameteriv方法查询指定缓冲区的信息,其方法签名如下。

1    public static void glGetBufferParameteriv (int target, int pname, IntBuffer params)
2    public static void glGetBufferParameteriv (int target, int pname, int[] params, int offset)

说明

参数target用于描述指定的缓冲区类型(如表1-1所列);参数pname为要查询的信息项目,其可能的取值如表1-3所列;params参数用于存放查询的结果。

提示

当第一次调用glBindBuffer方法绑定缓冲区对象时,GL_BUFFER_SIZE初始值为0。GL_BUFFER_USAGE的详细取值如表1-2所列,初始值为GL_STATIC_DRAW。

1.1.2 一个简单的案例

了解了顶点缓冲区的基本知识以后,下面将给出一个使用了顶点缓冲区的简单案例——Sample1_1,其具体运行效果如图1-1所示。

提示

运行本案例时,当手指在屏幕上上下左右滑动时,相应的物体也会绕x轴和y轴旋转。

了解了本小节案例的运行效果后,就可以进行案例的开发了,具体步骤如下。

(1)首先用3dsMax生成两个基本物体(茶壶和软管),贴好纹理,并导出生成obj文件放入项目的assets目录下待用。

(2)开发出搭建场景的基本代码,包括加载物体、摆放物体、计算光照等。这些代码与上卷中许多案例中的基本套路完全一致,这里不再赘述。

(3)顶点缓冲区对象主要用于存储物体的顶点数据,以备在绘制时使用,与其相关的操作都在代表加载物体的LoadedObjectVertexNormalTexture类中,具体内容如下。

1    package com.bn.Sample1_1;
2    ……//此处省略了导入一些相关类的代码,请读者自行查看随书中的源代码
3    public class LoadedObjectVertexNormalTexture{
4        ……//此处省略了部分成员变量声明的代码,请读者自行查看随书中的源代码
5        int maPositionHandle;                           //顶点位置属性引用
6        int maNormalHandle;                             //顶点法向量属性引用
7        int maTexCoorHandle;                            //顶点纹理坐标属性引用
8        int mVertexBufferId;                            //顶点坐标数据缓冲编号
9        int mNormalBufferId;                            //顶点法向量数据缓冲编号
10        int mTexCoorBufferId;                          //顶点纹理坐标数据缓冲编号
11        public LoadedObjectVertexNormalTexture(
12                  MySurfaceView mv,float[] vertices,float[] normals,float texCoors[]){
13               //调用初始化顶点数据的方法
14               initVertexData(vertices,normals,texCoors);
15               //调用初始化着色器的方法
16               initShader(mv);
17        }
18        public void initVertexData(float[] vertices,float[] normals,float texCoors[]){
19                int[] buffIds=new int[3];                  //缓冲编号数组
20                GLES30.glGenBuffers(3, buffIds, 0);        //生成3个缓冲
21                mVertexBufferId=buffIds[0];                //顶点坐标数据缓冲编号
22                mNormalBufferId=buffIds[1];                //顶点法向量数据缓冲编号
23                mTexCoorBufferId=buffIds[2];               //顶点纹理坐标数据缓冲编号
24                vCount=vertices.length/3;                  //计算顶点数量
25                ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
                                                 //创建顶点坐标数据缓冲
26                vbb.order(ByteOrder.nativeOrder());        //设置字节顺序
27                FloatBuffer mVertexBuffer = vbb.asFloatBuffer();//转换为Float型缓冲
28                mVertexBuffer.put(vertices);              //向缓冲区中放入顶点坐标数据
29                mVertexBuffer.position(0);                //设置缓冲区起始位置
30                //绑定到顶点坐标数据缓冲
31                GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mVertexBufferId);
32                //向顶点坐标缓冲中送入数据
33                GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,
34                vertices.length*4, mVertexBuffer, GLES30.GL_STATIC_DRAW);
35                ……//此处纹理坐标数据相关代码与上述代码相似,请读者自行查看随书中的源代码
36                ……//此处法向量数据相关代码与上述代码相似,请读者自行查看随书中的源代码
37               GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0);    //绑定到系统默认缓冲
38        }
39        public void initShader(MySurfaceView mv){/*代码省略*/}
40        public void drawSelf(int texId){ /*此处省略drawSelf方法,将在后面详细介绍*/}
41    }
  • 第1~第17行为成员变量的声明以及构造器代码,这部分代码大部分与上卷介绍obj模型加载的案例相同,主要是增加了声明缓冲区编号的3个成员变量。
  • 第19~第23行通过调用glGenBuffers方法创建了三个缓冲,并将获得的缓冲编号存储到了对应的成员变量中。
  • 第25~第29行创建了内存缓冲,并将顶点坐标数据存入内存缓冲中,以备后面将内存缓冲中的顶点坐标数据送入顶点缓冲区。
  • 第30~第34行首先绑定了顶点坐标缓冲,然后将内存缓冲中的顶点坐标数据送入对应的顶点缓冲区中,以备在绘制物体时使用。

(4)使用了顶点缓冲区之后,绘制物体的drawSelf方法有一些变化,那就是在每次绘制物体时不需要将顶点数据重复送入渲染管线了,而是直接使用前面步骤中初始化时存放到顶点缓冲区中的相关数据进行绘制,具体代码如下。

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.glEnableVertexAttribArray(maPositionHandle); //启用顶点位置数据数组
12         GLES30.glEnableVertexAttribArray(maNormalHandle);   //启用法向量数据数组
13         GLES30.glEnableVertexAttribArray(maTexCoorHandle);  //启用纹理坐标数据数组
14         //绑定到顶点坐标数据缓冲
15         GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mVertexBufferId);
16         //指定顶点位置数据使用对应缓冲
17         GLES30.glVertexAttribPointer (maPositionHandle,3, GLES30.GL_FLOAT, false,3*4,0);
18         //绑定到顶点法向量数据缓冲
19         GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mNormalBufferId);
20         //指定顶点法向量数据使用对应缓冲
21         GLES30.glVertexAttribPointer  (maNormalHandle, 3, GLES30.GL_FLOAT, false, 3*4,0);
22         //绑定到顶点纹理坐标数据缓冲
23         GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mTexCoorBufferId);
24         //指定顶点纹理坐标数据使用对应缓冲
25         GLES30.glVertexAttribPointer(maTexCoorHandle, 2, GLES30.GL_FLOAT, false, 2*4,0 );
26         GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0);         //绑定到系统默认缓冲
27         GLES30.glActiveTexture(GLES30.GL_TEXTURE0);            //激活纹理
28         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId);     //绑定纹理
29         GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vCount);   //绘制加载的物体
30    }
  • 第2~第13行指定使用某套着色器程序,将最终变换矩阵、基本变换矩阵、光源位置、摄像机位置等传入渲染管线,并启用顶点位置数据数组、法向量数据数组、纹理坐标数据数组。这部分代码与前面很多案例中的代码相同。
  • 第14~第26行首先绑定到顶点坐标数据缓冲,并指定绘制时的顶点位置数据从此缓冲中获取,接着绑定到顶点法向量数据缓冲,并指定绘制时顶点法向量数据从此缓冲中获取。然后绑定到顶点纹理坐标数据缓冲,并指定绘制时顶点纹理坐标数据从此缓冲中获取。最后绑定到系统默认缓冲,这一步初学者容易忘记,请读者注意。
  • 第27~第29行激活并绑定了所需的纹理,然后进行物体的绘制,与前面很多案例相同。

提示

从上述案例可以看出,将普通的每次绘制时送入顶点数据的应用修改为使用顶点缓冲一次送入数据的版本很容易。只需要在初始化时将数据送入对应的顶点缓冲,在绘制时指定使用即可,整体代码变化不大。但运行效率会有明显差别,因此在实际开发中读者应该尽量使用缓冲。

时间: 2024-11-02 08:27:38

《OpenGL ES 3.x游戏开发(下卷)》一1.1 顶点缓冲区的相关文章

《OpenGL ES 3.x游戏开发(下卷)》一导读

前 言 OpenGL ES 3.x游戏开发(下卷) 为什么要写这样一套书 随着智能手机硬件性能的不断提升,如"水果忍者""极品飞车""狂野飙车8:极速凌云"等一批优秀的3D游戏娱乐应用在广大智能机用户间流行开来.与此同时,也带动了手机游戏产业逐渐从2D走向3D.但目前国内专门介绍3D游戏开发的书籍与资料都非常少,同时3D应用开发的门槛又比较高,使得很多初学者无从下手.根据这种情况,笔者结合多年从事3D游戏应用开发的经验编写了这样一套书. 了解一些

《OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》——6.4节点法向量和面法向量

6.4 点法向量和面法向量 OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例 本章前面几节的案例都是基于球面开发的,球面属于连续.平滑的曲面,因此面上的每个顶点都有确定的法向量.但现实世界中的物体表面并不都是连续.平滑的,此时对于面上的某些点的法向量计算就不那么直观了,图6-18说明了这个问题. 从图6-18中可以看出,顶点A位于长方体左.上.前3个面的交界处,此处是不光滑的.这种情况下顶点A的法向量有两种处理策略,具体如下所列. 在顶点A的位置放置3个不同的顶点,每个顶点看作是仅

《OpenGL ES 3.x游戏开发(上卷)》一第1章 Android概述

第1章 Android概述 OpenGL ES 3.x游戏开发(上卷)随着移动互联网时代的到来,智能手机逐渐走进了人们的生活, Google公司于2007年11月5日发布了基于Linux平台的开源手机操作系统--Android.由于Android系统的开源性以及其他各个方面的因素,其受到了广大手机厂商的青睐,因此需要大量的Android开发人员来满足日益增长的海量软件开发需求. 提示 有些读者可能会有一点奇怪,不是介绍OpenGL ES 3.0的知识吗,怎么一开始就介绍Android呢?这是因为

《OpenGL ES 3.x游戏开发(上卷)》一第2章 游戏开发相关的 Android基础知识

第2章 游戏开发相关的 Android基础知识 OpenGL ES 3.x游戏开发(上卷)虽然本书主要是介绍OpenGL ES 3.0 3D应用及游戏开发的,但由于很多3D游戏应用中还需要用到目标平台的一些其他应用开发方面的知识,而本书3D基础知识部分主要是借助于Android平台来介绍OpenGL ES 3.0 3D应用及游戏开发的.故本章将向读者简要介绍一些在Android游戏开发中必备的一些基础知识,如音效.文件的读取.游戏信息的存储.Socket网络.蓝牙网络等. 提示 由于介绍Andr

OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》一6.3 定位光与定向光

6.3 定位光与定向光 OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例上一节中介绍的光照效果都是基于定位光光源的,定位光光源类似于现实生活中的白炽灯灯泡,其在某个固定的位置,发出的光向四周发散.定位光照射的一个明显特点就是,在给定光源位置的情况下,对不同位置的物体产生的光照效果不同. 现实世界中并不都是定位光,例如照射到地面上的阳光,光线之间是平行的,这种光称为定向光.定向光照射的明显特点是,在给定光线方向的情况下,场景中不同位置的物体反映出的光照效果完全一致.图6-16中对定位

《OpenGL ES 3.x游戏开发(下卷)》一1.2 顶点数组对象

1.2 顶点数组对象 使用了顶点缓冲技术后,绘制效率有了较大的提升.但是还有一点不尽如人意,那就是顶点的位置坐标.法向量.纹理坐标等不同方面的数据每次使用时需要单独指定,重复了一些不必要的工作.OpenGL ES 3.0考虑到了这一点,提供了一种专门用于解决此问题的对象--顶点数组对象(VAO).本节将介绍顶点数组对象. 1.2.1 基本知识与案例效果 顶点数组对象的主要功能就是将绘制一个物体时所需的对应于不同方面(如顶点坐标.法向量.纹理坐标)的顶点缓冲及相关设置包装成一个整体,绘制时直接使用

《OpenGL ES 3.x游戏开发(下卷)》一2.7 固定渲染管线与可编程渲染管线实现方案的对比

2.7 固定渲染管线与可编程渲染管线实现方案的对比 本章最开始提到过,在固定渲染管线平台上想高效地实现本章案例中的特效是非常困难的.这是因为在固定渲染管线中,顶点数据一旦送入渲染管线后就不可能对其方便地自定义处理了.因此,在固定渲染管线上想实现本章案例中的特效只能采用以下两种策略之一. 提示 回顾一下,OpenGL ES 1.x(含1.0和1.1)采用的是固定渲染管线,从OpenGL ES 2.0开始采用可编程渲染管线. 1.初始化时预先计算数据此种策略的基本思想非常简单,就是在初始化时将动画中

《OpenGL ES 3.x游戏开发(下卷)》一1.5 其他缓冲区对象操作

1.5 其他缓冲区对象操作 前面已经介绍了顶点缓冲区对象.顶点数组对象.一致缓冲区对象以及映射缓冲区对象,本节将介绍两个其他的缓冲区对象操作,包括复制缓冲区对象(Copying Buffer Objects)以及从颜色缓冲区复制纹理数据(Copying Texture Data from the Color Buffer). 1.5.1 复制缓冲区对象 到目前为止,已经介绍了如何使用glBufferData.glBufferSubData以及 glMapBufferRange方法将数据加载到缓冲

《OpenGL ES 3.x游戏开发(下卷)》一1.8 小结

1.8 小结 本章主要介绍了顶点缓冲区对象.顶点数组对象.一致缓冲区对象.映射缓冲区对象.复制缓冲区对象.从颜色缓冲区复制纹理数据.帧缓冲与渲染缓冲以及多重渲染目标等关于缓冲的基本知识与技术,并给出了一些典型的小案例. 通过本章的学习,读者可以初步领会缓冲区对象所起的重要作用,为以后开发更加复杂.更高性能的3D应用或游戏奠定了良好的基础.