关键字: android OpenGL 移动开发 教程
在第一课的基础上,我们添加了一个三角形和一个四边形,在这里我们使用glDrawElements方法来绘制我们的第一个三角形和使用glDrawArrays来绘制我们的第一个四边形,来演示在OpenGL ES中怎样绘制最基本的图元。
在OpenGL ES中不支持glVertex方法,因此在绘制三角形和四边形的方法和NeHe教程中的方法有所不同。为了是程序条理清楚,我们分别实现MyTriangle和MySquare来绘制我们的一个三角形和四边形。
现在假设MyTriangl和 MySquare已经实现好,在后面详细叙述这两个类的实现。在MyGLRender的onDrawFrame中的代码如下:
public void onDrawFrame(GL10 gl) { // 清除屏幕和深度缓存 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // 重置当前的模型观察矩阵 gl.glLoadIdentity(); // 左移 1.5 单位,纵深向里移动 6.0 gl.glTranslatef(-1.5f,0.0f,-6.0f); // 画三角形 triangle.draw(gl); // 右移3单位 gl.glTranslatef(3.0f,0.0f,0.0f); // 画四边形 quad.draw(gl); }
当我们调用glLoadIdentity ()之后,实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
glTranslatef(x, y, z)沿着 X, Y和 Z轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。注意在glTranslatef(x, y, z)中移动的时候,它并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。
现在我们已经移到了屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。triangle.draw(gl)来完成我们的第一个三角形绘制。
在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。为此要再次使用glTranslate。这次右移,所以X坐标值为正值。因为前面左移了1.5个单位,这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。总共要向右移3.0个单位。quad.draw(gl)来完成我们的第一四边形的绘制。
MyTriangle.java:
package wintop.gllesson02; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; // 一个三角形包含三个顶点 public class MyTriangle { private FloatBuffer vertexBuffer; // 顶点数组缓冲区 private ByteBuffer indexBuffer; // 顶点索引缓冲区 private float[] vertices = { // 三角形的顶点 0.0f, 1.0f, 0.0f, // 0. 顶 -1.0f, -1.0f, 0.0f, // 1. 左下角 1.0f, -1.0f, 0.0f // 2. 右下角 }; private byte[] indices = {0, 1, 2}; // 顶点索引(逆时针方向 CCW) // 构造函数 - 设置顶点数组 public MyTriangle(){ // 设置顶点数组,顶点数据为浮点数据类型。一个浮点类型的数据长度为四个字节 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); // 使用原生字节顺序 vertexBuffer = vbb.asFloatBuffer(); // 将字节类型缓冲区转换成浮点类型 vertexBuffer.put(vertices); // 将数据复制进缓冲区 vertexBuffer.position(0); // 定位到初始位置 // 设置索引数组,索引数据位字节类型 indexBuffer = ByteBuffer.allocateDirect(indices.length); indexBuffer.put(indices); indexBuffer.position(0); } // 渲染三角形 public void draw(GL10 gl) { // 使能顶点数据并指定顶点数据缓冲区 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); // 通过索引数组绘制图元 gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } }
然后我们分配一个顶点数组缓冲区,并将数据传送到缓冲区中。在这里我们使用nio类型的缓冲区,这是因为这种类型的缓冲区位于原生堆中,不会使用垃圾收集机制。
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder()); // 使用原生字节顺序
vertexBuffer = vbb.asFloatBuffer(); //将字节类型缓冲区转换成浮点类型
vertexBuffer.put(vertices); //将数据复制进缓冲区
vertexBuffer.position(0); //定位到初始位置
为了使用顶点数组渲染方式,我们必须使能client-state vertex-array:
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
我们可以直接使用glDrawArrays()方法来直接渲染顶点数组,或者使用glDrawElements()方法通过索引数组来渲染。
在这里我们使用了索引数组,顶点按逆时针方向(CCW)进行索引,法向为屏幕向外(正Z轴方向)。
// 设置索引数组,索引数据位字节类型
indexBuffer = ByteBuffer.allocateDirect(indices.length);
indexBuffer.put(indices);
indexBuffer.position(0);
在draw()方法中实现我们第一个三角形的渲染。
首先,我们使能client-state vertex-array
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
然后,我们通过缓冲区指定位置:
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
// gl*Pointer(int size, int type, int stride, Buffer pointer)
// size: 每个顶点的坐标数目 (必须为 2, 3, or 4).
// type: 顶点坐标数据类型, GL_BYTE, GL_SHORT, GL_FIXED, or GL_FLOAT
// stride: 两个连续顶点间偏移的字节数.
最后我们使用glDrawElements()来绘制三角形。该方法使用索引数组来引用顶点和颜色值。
gl.glDrawElements(GL10.GL_TRIANGLES, numIndices, GL10.GL_UNSIGNED_BYTE, indexBuffer);
// glDrawElements(int mode, int count, int type, Buffer indices)
// mode: GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES
// count: 要渲染的顶点数目.
// type: 索引数据类型(必须为 GL_UNSIGNED_BYTE或 GL_UNSIGNED_SHORT).
// indices: 索引数组指针.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
MySquare.java
package wintop.gllesson02; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; // 使用三角形带(TRIANGLE_STRIP)绘制一个正方形 public class MySquare { private FloatBuffer vertexBuffer; // 顶点数组缓冲区 private float[] vertices = { // 正方形的顶点数据 -1.0f, -1.0f, 0.0f, // 0.左下角 1.0f, -1.0f, 0.0f, // 1.右下角 -1.0f, 1.0f, 0.0f, // 2.左上角 1.0f, 1.0f, 0.0f, // 3.右上角 }; // 构造函数 - 设置顶点缓冲区 public MySquare() { // 设置顶点数组,顶点数据为浮点数据类型。一个浮点类型的数据长度为四个字节 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); // 使用原生字节顺序 vertexBuffer = vbb.asFloatBuffer(); // 将字节类型缓冲区转换成浮点类型 vertexBuffer.put(vertices); // 将数据复制进缓冲区 vertexBuffer.position(0); // 定位到初始位置 } // 渲染正方形 public void draw(GL10 gl){ // 使能顶点数据并指定顶点数据缓冲区 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); // 通过顶点数组直接绘制图元序列 gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length/3); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } }
在OpenGL ES 中四边形不是图元,因为我们需要用两个三角形来实现绘制四边形。我们使用TRIANGLE_STRIP,按逆时针方向组合两个三角形V0V1V2和V2V1V3。如下图所示
最后运行结果如图所示:
源代码下载地址:http://download.csdn.net/detail/seniorwizard/4460814