关键字: 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);   // 剔除背面(不显示)

Android OpenGL编程 android opengl教程_Android OpenGL编程

立方体的面顶点排列方式


最终运行结果如下图所示:

Android OpenGL编程 android opengl教程_Android OpenGL编程_02