最近在写毕设的时候用到OpenGL ES中的VBO,由于对一些接口用到的变量不了解被坑得很惨,在此记录一下防止以后再被坑。
使用VBO的好处在此就不多说了,在Java中操作VBO绘图涉及到的OpenGL接口主要有以下几个:
1. void glGenBuffers(int n, int[] buffers, int offset)
向OpenGL ES申请开辟新的VBO,并通过buffers数组获取VBO handle,handle的类型为整型。
int n 申请的VBO个数
int[] buffers 用于存储VBO handle的数组
int offset buffers数组的偏移量,即从buffers的第offset个位置开始存储handle
注意需要满足 n + offset <= buffers.length
2. void glBindBuffer(int target, int buffer)
通过handle绑定指定的VBO,同一时间只能绑定一个同类型的VBO,只有当前被绑定的VBO才会被用户操作。通过绑定handle为0的VBO,可以取消对所有同类型VBO的绑定。
int target 指定绑定的VBO类型,具体类型有GL_ARRAY_BUFFER(用于为顶点数组传值)和GL_ELEMENT_ARRAY_BUFFER(用于为索引数组传值)
int buffer 指定绑定的VBO handle
3. void glBufferData(int target, int size, Buffer data, int usage)
将数据传递给当前绑定的VBO。
int target 指定VBO类型,同上
int size 指定VBO的大小,单位为bytes
Buffer data 指定需要传递的数据
int usage 指定VBO的存储方式,例如GL_STATIC_DRAW或GL_DYNAMIC_DRAW
4. void glVertexAttribPointer(int indx, int size, int type, boolean normalized, int stride, int offset)
将VBO中的数据传递给顶点数组。
int indx 指定Shader属性的顶点数组handle
int size 指定该属性的顶点数组大小,单位为数组元素的类型
int type 指定该属性的顶点数组元素类型,如GL_FLOAT和GL_UNSIGNED_BYTE
boolean normalize 指定传递给该属性顶点数组的数据是否需要归一化(转化为单位向量)
int stride 指定该属性的顶点数据在VBO中的跃度,即每个顶点所占的数据长度,单位为bytes
int offset 指定该属性在VBO中起始位置的偏移量,单位为bytes
5. void glDrawArrays(int mode, int first, int count)
直接使用顶点数组绘制图元。
int mode 指定绘图的模式,如GL_TRIANGLES和GL_TRIANGLE_STRIPS
int first 指定从第几个顶点开始绘制
int count 指定绘制几个顶点
类似功能的函数还有void glDrawElements(int mode, int count, int type, int offset),此函数通过使用索引数组绘制图元。
通过下面的简单实例中可以了解这些方法在实际操作中如何使用:
public void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); // 设置旋转矩阵的函数,在此可以忽略 setRotationView(0); // 获取Position属性的句柄 int positionHandle = GLES20.glGetAttribLocation(programHandle, "Position"); // 获取SourceColor属性的句柄 int colorHandle = GLES20.glGetAttribLocation(programHandle, "SourceColor"); // 激活两个属性的数组 GLES20.glEnableVertexAttribArray(positionHandle); GLES20.glEnableVertexAttribArray(colorHandle); // 每个顶点的跃度,即一个顶点占有的数据类型个数 int stride = 6; // 顶点数据数组,6个float组成一个顶点,前2个float为位置坐标,后4个float为颜色RGBA float[] data = { -0.5f, -0.5f, 1f, 1f, 1f, 1f, 0.5f, -0.5f, 1f, 1f, 1f, 1f, 0f, 1f, 1f, 1f, 1f, 1f }; // 将顶点数组封装进Buffer中 // 值得注意的一点是通过Buffer.wrap()方法生成的Buffer无法在OpenGL ES中使用,必须通过如下方法创建Buffer ByteBuffer byteBuffer = ByteBuffer.allocateDirect(data.length * 4); // OpenGL ES中使用的数据为小端字节序(低位字节在前,高位字节在后),而Java的Buffer默认使用大端字节序(高位字节在前,低位字节在后)存储数据,所以在此需要通过下面的方法进行转换 byteBuffer.order(ByteOrder.nativeOrder()); // 将ByteBuffer转换为FloatBuffer FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); // 将data中的数据放入FloatBuffer中 floatBuffer.put(data); // 重新定义Buffer的起点和终点,等价于同时使用postion(0)方法和limit(data.length)方法 floatBuffer.flip(); // 用于获取VBO handle的临时变量 int[] temp = new int[1]; // 向OpenGL申请新的VBO,将handle存于temp中 GLES20.glGenBuffers(1, temp, 0); // 从temp中取出VBO handle vertexBufferHandle = temp[0]; // 绑定刚刚申请到的VBO GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferHandle); // 将FLoatBuffer中的数据传递给OpenGL ES GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, floatBuffer.limit() * 4, floatBuffer, GLES20.GL_STATIC_DRAW); // 将VBO中的数据传递给shader中的顶点数组 GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, stride * 4, 0); GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, stride * 4, 2 * 4); // 绘制三角形 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); // 取消buffer的绑定 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); // 反激活顶点数组 GLES20.glDisableVertexAttribArray(positionSlot); GLES20.glDisableVertexAttribArray(colorSlot); }
关于注释中提到的大端字节序和小端字节序,具体可以查阅下篇博文:
http://www.cnblogs.com/xiehy/archive/2010/11/25/1887779.html