关键字: android OpenGL 移动开发 教程
在上节课的基础上,我们现在开始生成真正的3D对象,而不是象前两节课中那样3D世界中的2D对象。我们给三角形增加一个左侧面,一个右侧面,一个后侧面来生成一个金字塔(四棱锥)。给正方形增加左、右、上、下及背面生成一个立方体。我们混合金字塔上的颜色,创建一个平滑着色的对象。给立方体的每一面则来个不同的颜色。这样在我们的工程中MyTriangle类变成了MyPyramid , MySquare变成了MyCube。
MyPyramid.java
package wintop.gllesson05;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class MyPyramid {
private FloatBuffer vertexBuffer; // 顶点数组缓冲区
private FloatBuffer colorBuffer; // 顶点颜色缓冲区
private ByteBuffer indexBuffer; // 索引数组缓冲区
private float[] vertices = { // 在(x,y,z)坐标系中金字塔的五个顶点坐标
-1.0f, -1.0f, -1.0f, // 0. 左-底-后
1.0f, -1.0f, -1.0f, // 1. 右-底-后
1.0f, -1.0f, 1.0f, // 2. 右-底-前
-1.0f, -1.0f, 1.0f, // 3. 左-底-前
0.0f, 1.0f, 0.0f // 4. 顶
};
private float[] colors = { // 五个顶点的颜色(RGBA模式)
0.0f, 0.0f, 1.0f, 1.0f, // 0. 蓝
0.0f, 1.0f, 0.0f, 1.0f, // 1. 绿
0.0f, 0.0f, 1.0f, 1.0f, // 2. 蓝
0.0f, 1.0f, 0.0f, 1.0f, // 3. 绿
1.0f, 0.0f, 0.0f, 1.0f // 4. 红
};
private byte[] indices = { // 四个三角形的顶点索引
2, 4, 3, // 前面 (CCW)
1, 4, 2, // 右面
0, 4, 1, // 后面
4, 0, 3 // 左面
};
// 构造函数 - 设置缓冲区
public MyPyramid() {
// 设置顶点数组,顶点数据为浮点数据类型。一个浮点类型的数据长度为四个字节
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder()); // 使用原生字节顺序
vertexBuffer = vbb.asFloatBuffer(); // 将字节类型缓冲区转换成浮点类型
vertexBuffer.put(vertices); // 将数据复制进缓冲区
vertexBuffer.position(0); // 定位到初始位置
// 设置颜色缓冲区. 颜色数据为浮点数据类型。一个浮点类型的数据长度为四个字节
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
cbb.order(ByteOrder.nativeOrder());
colorBuffer = cbb.asFloatBuffer();
colorBuffer.put(colors);
colorBuffer.position(0);
// 设置索引缓冲区. 索引数据为字节数据类型.
indexBuffer = ByteBuffer.allocateDirect(indices.length);
indexBuffer.put(indices);
indexBuffer.position(0);
}
// 绘图
public void draw(GL10 gl) {
gl.glFrontFace(GL10.GL_CCW); // 设置正前面为逆时针方面
// 数组使能并定义它们各自的缓冲区
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE,indexBuffer);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
}
}
MyCube.java
package wintop.gllesson05;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class MyCube {
private FloatBuffer vertexBuffer; // 顶点数组缓冲区
private int numFaces = 6;
private float[][] colors = { // 六个面的颜色
{1.0f, 0.5f, 0.0f, 1.0f}, // 0. 橙色
{1.0f, 0.0f, 1.0f, 1.0f}, // 1. 紫色
{0.0f, 1.0f, 0.0f, 1.0f}, // 2. 绿色
{0.0f, 0.0f, 1.0f, 1.0f}, // 3. 蓝色
{1.0f, 0.0f, 0.0f, 1.0f}, // 4. 红色
{1.0f, 1.0f, 0.0f, 1.0f} // 5. 黄色
};
private float[] vertices = { // 六个面的顶点坐标
// 前
-1.0f, -1.0f, 1.0f, // 0. 左-底-前
1.0f, -1.0f, 1.0f, // 1. 右-底-前
-1.0f, 1.0f, 1.0f, // 2. 左-顶-前
1.0f, 1.0f, 1.0f, // 3. 右-顶-前
// 后
1.0f, -1.0f, -1.0f, // 6. 右-底-后
-1.0f, -1.0f, -1.0f, // 4. 左-底-后
1.0f, 1.0f, -1.0f, // 7. 右-顶-后
-1.0f, 1.0f, -1.0f, // 5. 左-顶-后
// 左
-1.0f, -1.0f, -1.0f, // 4. 左-底-后
-1.0f, -1.0f, 1.0f, // 0. 左-底-前
-1.0f, 1.0f, -1.0f, // 5. 左-顶-后
-1.0f, 1.0f, 1.0f, // 2. 左-顶-前
// 右
1.0f, -1.0f, 1.0f, // 1. 右-底-前
1.0f, -1.0f, -1.0f, // 6. 右-底-后
1.0f, 1.0f, 1.0f, // 3. 右-顶-前
1.0f, 1.0f, -1.0f, // 7. 有-顶-后
// 上
-1.0f, 1.0f, 1.0f, // 2. 左-顶-前
1.0f, 1.0f, 1.0f, // 3. 右-顶-前
-1.0f, 1.0f, -1.0f, // 5. 左-顶-后
1.0f, 1.0f, -1.0f, // 7. 右-顶-后
// 下
-1.0f, -1.0f, -1.0f, // 4. 左-底-后
1.0f, -1.0f, -1.0f, // 6. 右-底-后
-1.0f, -1.0f, 1.0f, // 0. 左-底-前
1.0f, -1.0f, 1.0f // 1. 右-底-后
};
// 构造函数 - 设置缓冲区
public MyCube() {
// 设置顶点数组,顶点数据为浮点数据类型。一个浮点类型的数据长度为四个字节
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.glFrontFace(GL10.GL_CCW); // 正前面为逆时针方向
gl.glEnable(GL10.GL_CULL_FACE); // 使能剔除面
gl.glCullFace(GL10.GL_BACK); // 剔除背面(不显示)
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
// 渲染所有的面
for (int face = 0; face < numFaces; face++) {
// 设置每个面的颜色
gl.glColor4f(colors[face][0], colors[face][1], colors[face][2], colors[face][3]);
// 根据顶点数据直接绘制图元
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, face*4, 4);
}
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisable(GL10.GL_CULL_FACE);
}
}
立方体所有面相对应法向法向都是按逆时针方向排列。这样使我们可以进行背面剔除操作,背面剔除操作的代码如下:
gl.glFrontFace(GL10.GL_CCW); // 正前面为逆时针方向
gl.glEnable(GL10.GL_CULL_FACE);// 使能剔除面
gl.glCullFace(GL10.GL_BACK); // 剔除背面(不显示)
立方体的面顶点排列方式
最终运行结果如下图所示: