Android OpenGL 使用

  1. 基本框架
    编写一个类来继承GLSurfaceView.Renderer,并实现其中的三个方法onSurfaceCreated、onSurfaceChanged、onDrawFrame。

/**
 * 基本框架
 * Created by mazaiting on 2017/8/9.
 */
public class GLRenderer implements GLSurfaceView.Renderer {

  /**
   * 在窗口被创建时被调用,需要做一些必要的初始化工作:
   */
  @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // 启动阴影平滑
    gl.glShadeModel(GL10.GL_SMOOTH);
    // 黑色背景,设置清楚屏幕时所用的颜色取值:RGBA.0f-1.0f
    gl.glClearColor(0, 0, 0, 1.0f);
    // 设置深度缓存--决定哪个物体先画
    gl.glClearDepthf(1.0f);
    // 启动深度测试
    gl.glEnable(GL10.GL_DEPTH_TEST);
    // 所作深度测试的类型
    gl.glDepthFunc(GL10.GL_LEQUAL);
    // 告诉系统对透视进行修正
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
  }

  /**
   * 当窗口大小发生改变时被调用,不管窗口的大小是否已经改变,在程序开始时至少运行一次。
   */
  @Override public void onSurfaceChanged(GL10 gl, int width, int height) {
    float ratio = (float) width / height;
    // 设置OpenGL场景的大小
    gl.glViewport(0, 0, width, height);
    // 设置投影矩阵--增加透视
    gl.glMatrixMode(GL10.GL_PROJECTION);
    // 重置投影矩阵--恢复原始状态
    gl.glLoadIdentity();
    // 设置视图的大小
    gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    // 选择模型观察矩阵
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    // 重置模型观察矩阵
    gl.glLoadIdentity();
  }

  /**
   * 在窗口内进行绘图操作。 在绘图之前,需要将屏幕清楚成前面指定的颜色,清楚深度缓存并且重置场景
   */
  @Override public void onDrawFrame(GL10 gl) {
    // 清楚屏幕和深度缓存
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    // 重置当前的模型观察矩阵
    gl.glLoadIdentity();
    // 具体绘图开始...

  }

  /**
     * OpenGL 是一个非常底层的画图接口,它所使用的缓冲区存储结构是和我们的 java 程序中不相同的。
     * Java 是大端字节序(BigEdian),而 OpenGL 所需要的数据是小端字节序(LittleEdian)。
     * 所以,我们在将 Java 的缓冲区转化为 OpenGL 可用的缓冲区时需要作一些工作。建立buff的方法如下
     **/
  public Buffer bufferUtil(int []arr){
    // 先初始化buffer,数组的长度*4,因为一个int占4个字节
    ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
    // 数组排列用nativeOrder
    qbb.order(ByteOrder.nativeOrder());
    // 将ByteBuffer转换为IntBuffer
    IntBuffer mBuffer = qbb.asIntBuffer();
    // 将数组设置进去
    mBuffer.put(arr);
    //mBuffer.position(0);
    // 重置
    mBuffer.flip();
    return mBuffer;
  }
}

其中都是一些具体的配置,在主Activity中,我们首先创建出一个GLRenderer对象 ,并创建GLSurfaceView对象,将GLRenderer对象设置在GLSurfaceView对象中。、


public class MainActivity extends AppCompatActivity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // 基础框架
    GLSurfaceView.Renderer renderer = new GLRenderer();
    GLSurfaceView glView = new GLSurfaceView(this);
    glView.setRenderer(renderer);
    // 设置布局
    setContentView(glView);
  }
}

至此,我们将OpenGL开发时所要做的配置已完成,接下来我们在写新的Renderer时,只需要继承在GLRenderer,重写onDrawFrame(GL10 gl)方法即可。

  1. 绘制多边形
    实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();的此行代码更改为GLSurfaceView.Renderer renderer = new PolygonRenderer();
/**
 * 绘制多边形
 * Created by mazaiting on 2017/8/9.
 */
public class PolygonRenderer extends GLRenderer{
  int one = 0x00010000;
  //三角形三个顶点
  private int[] triggerBuffer = new int[] {
      0, one, 0,//上顶点
      - one, -one, 0, //左下点
      one, -one, 0,}; //右下点

  //正方形的4个顶点
  private int[] quaterBuffer = new int[]{
      one,one,0,
      -one,one,0,
      one,-one,0,
      -one,-one,0};

  @Override public void onDrawFrame(GL10 gl) {
    super.onDrawFrame(gl);
    // 此函数,就是将画笔沿X轴左移1.5f个单位,Y轴保持不变,Z轴向屏幕里面移动6.0f个单位。
    //gl.glTranslatef(-1.5f, 0.0f, -6.0f);
    // 左移 1.5 单位,并移入屏幕 6.0
    gl.glTranslatef(-1.5f, 0.0f, -6.0f);// z 轴值小于-1.0f
    // 允许设置顶点
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    // 设置三角形
    gl.glVertexPointer(3, GL10.GL_FIXED, 0, bufferUtil(triggerBuffer));
    // 绘制三角形
    gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);

    // 重置当前模型观察矩阵
    gl.glLoadIdentity();
    // 右移 1.5 单位,并移入屏幕 6.0
    gl.glTranslatef(1.5f, 0.0f, -6.0f);// z 轴值小于-1.0f
    // 设置四边形
    gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(quaterBuffer));
    // 绘制四边形
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
    //取消顶点设置
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    // 重置当前的模型观察矩阵
    gl.glLoadIdentity();
  }
}

效果图:

绘制多边形.png
  1. 绘制颜色
    实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();的此行代码更改为GLSurfaceView.Renderer renderer = new ColorRenderer();
/**
 * 绘制颜色
 * Created by mazaiting on 2017/8/9.
 */
public class ColorRenderer extends GLRenderer {
  int one = 0x10000;
  //三角形三个顶点 (r,g,b,a)
  private int[] triggerBuffer = new int[] {
      0, one, 0,//上顶点
      - one, -one, 0, //左下点
      one, -one, 0,}; //右下点

  //正方形的4个顶点
  private int[] quaterBuffer = new int[]{
      one,one,0,
      -one,one,0,
      one,-one,0,
      -one,-one,0};
  //三角形的顶点颜色值(r,g,b,a)
  private int[] colorBuffer = new int[]{
      one,0,0,one,
      0,one,0,one,
      0,0,one,one,
      };

  @Override public void onDrawFrame(GL10 gl) {
    super.onDrawFrame(gl);
    // 左移 1.5 单位,并移入屏幕 6.0
    gl.glTranslatef(-1.5f,0.0f,-6.0f);
    //设置定点数组
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    //设置颜色数组 -- 开启颜色渲染功能.
    gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
    // 设置三角形顶点的颜色
    gl.glColorPointer(4,GL10.GL_FIXED,0,bufferUtil(colorBuffer));
    // 设置三角形顶点
    gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(triggerBuffer));
    gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
    //关闭颜色数组 -- 关闭颜色渲染功能.
    gl.glDisableClientState(GL10.GL_COLOR_ARRAY);

    gl.glLoadIdentity();
    // 右移 1.5 单位,并移入屏幕 6.0
    gl.glTranslatef(1.5f,0.0f,-6.0f);
    // 设置当前色为蓝色
    gl.glColor4f(0.0f,0.5f,1.0f,1.0f);
    //设置和绘制正方形
    gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(quaterBuffer));
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
    //取消顶点数组
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
  }
}

效果图:

绘制颜色.png
  1. 绘制旋转
    实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();的此行代码更改为GLSurfaceView.Renderer renderer = new RotateRenderer();
/**
 * 绘制旋转图形
 * Created by mazaiting on 2017/8/9.
 */
public class RotateRenderer extends GLRenderer {
  int one = 0x00010000;
  //三角形三个顶点
  private int[] triggerBuffer = new int[] {
      0, one, 0,//上顶点
      - one, -one, 0, //左下点
      one, -one, 0,}; //右下点

  @Override public void onDrawFrame(GL10 gl) {
    super.onDrawFrame(gl);

    gl.glTranslatef(-1.5f,0.0f,-6.0f);
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glRotatef(180f,0,0,0);//旋转180
    gl.glVertexPointer(3,GL10.GL_FIXED,0,bufferUtil(triggerBuffer));
    gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glLoadIdentity();
  }
}

效果图:

绘制旋转.png
  1. 绘制三棱锥
    实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();的此行代码更改为GLSurfaceView.Renderer renderer = new PyramidRenderer();
/**
 * 四棱锥
 * Created by mazaiting on 2017/8/9.
 */

public class PyramidRenderer extends GLRenderer {
  int one = 0x10000;

  // 四棱锥顶点数组:
  private int[] triggerBuffer = new int[] {
      0, one, 0,
      -one, -one, one,
      one, -one, one,

      0,one, 0,
      one,-one, one,
      one, -one, -one,

      0, one, 0,
      one, -one, -one,
      -one, -one, -one,

      0, one, 0,
      -one, -one, -one,
      -one, -one, one
  };

  /**
   * Every vertex has got its own color, described by 4 values
   * R(ed)
   * G(green)
   * B(blue)
   * A(lpha)
   */
  int colors[] = {
      0, 0, 0, one, one, 0, 0, one, one, one, 0, one, 0, one, 0, one, 0, 0, one, one, one, 0, one,
      one, one, one, one, one, 0, one, one, one,
  };

  /**
   * The last thing is that we need to describe some Triangles.
   * A triangle got 3 vertices.
   * The confusing thing is, that it is important in which order
   * the vertices of each triangle are described.
   * So describing a triangle through the vertices: "0, 4, 5"
   * will not result in the same triangle as: "0, 5, 4"
   * You probably ask: Why the hell isn't that the same ???
   * The reason for that is the call of: "gl.glFrontFace(gl.GL_CW);"
   * which means, that we have to describe the "visible" side of the
   * triangles by naming its vertices in a ClockWise order!
   * From the other side, the triangle will be 100% lookthru!
   * You can create a kind of magic mirror with that
   **/
  byte indices[] = {
      0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 4, 7, 6, 4, 6, 5, 3,
      0, 1, 3, 1, 2
  };

  @Override public void onSurfaceChanged(GL10 gl, int width, int height) {
    super.onSurfaceChanged(gl, width, height);
    // 设置透视范围
    GLU.gluPerspective(gl, 45.0f, ((float) width) / height, 0.1f, 10f);
  }
  float xRot = 0.0f;
  float yRot = 0.0f;
  @Override public void onDrawFrame(GL10 gl) {
    super.onDrawFrame(gl);
    gl.glMatrixMode(GL10.GL_MODELVIEW);// 切换至模型观察矩阵
    gl.glLoadIdentity();// 重置当前的模型观察矩阵
    GLU.gluLookAt(gl, 0f, 0f, 3f, 0f, 0f, 0f, 0f, 1f, 0f);//设置视点和模型中心位置

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(3, GL10.GL_FIXED, 0, bufferUtil(triggerBuffer));
    gl.glRotatef(xRot,1f,0f,0f);// 绕着(0,0,0)与(1,0,0)即x轴旋转
    gl.glRotatef(yRot,0f,1f,0f);

    gl.glColor4f(1.0f,0.0f,0.0f,1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
    gl.glColor4f(0.0f,1.0f,0.0f,1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 3, 3);
    gl.glColor4f(0.0f,0.0f,1.0f,1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 6, 3);
    gl.glColor4f(1.0f,0.0f,1.0f,1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 9, 3);

    xRot += 1.0f;
    yRot += 0.5f;
  }
}

效果图:

三棱锥.png
  1. 绘制正方体
    实现此类后,要想看到效果,务必将主AcitivityGLSurfaceView.Renderer renderer = new GLRenderer();的此行代码更改为GLSurfaceView.Renderer renderer = new CubeRenderer();

/**
 * 绘制正方体
 * Created by mazaiting on 2017/8/10.
 */
public class CubeRenderer extends GLRenderer{

  float box[] = new float[] {
      // FRONT
      -0.5f, -0.5f,  0.5f,
      0.5f, -0.5f,  0.5f,
      -0.5f,  0.5f,  0.5f,
      0.5f,  0.5f,  0.5f,
      // BACK
      -0.5f, -0.5f, -0.5f,
      -0.5f,  0.5f, -0.5f,
      0.5f, -0.5f, -0.5f,
      0.5f,  0.5f, -0.5f,
      // LEFT
      -0.5f, -0.5f,  0.5f,
      -0.5f,  0.5f,  0.5f,
      -0.5f, -0.5f, -0.5f,
      -0.5f,  0.5f, -0.5f,
      // RIGHT
      0.5f, -0.5f, -0.5f,
      0.5f,  0.5f, -0.5f,
      0.5f, -0.5f,  0.5f,
      0.5f,  0.5f,  0.5f,
      // TOP
      -0.5f,  0.5f,  0.5f,
      0.5f,  0.5f,  0.5f,
      -0.5f,  0.5f, -0.5f,
      0.5f,  0.5f, -0.5f,
      // BOTTOM
      -0.5f, -0.5f,  0.5f,
      -0.5f, -0.5f, -0.5f,
      0.5f, -0.5f,  0.5f,
      0.5f, -0.5f, -0.5f,
  };

  FloatBuffer cubeBuff;
  float xRot = 0.0f;
  float yRot = 0.0f;

  public CubeRenderer(){
    cubeBuff = makeFloatBuffer(box);//转换为float数组
  }

  @Override public void onSurfaceChanged(GL10 gl, int width, int height) {
    super.onSurfaceChanged(gl, width, height);
    // 设置透视范围
    GLU.gluPerspective(gl,45.0f,((float)width)/height,0.1f,10f);
  }

  @Override public void onDrawFrame(GL10 gl) {
    super.onDrawFrame(gl);

    gl.glMatrixMode(GL10.GL_MODELVIEW);// 切换至模型观察矩阵
    gl.glLoadIdentity();// 重置当前的模型观察矩阵
    GLU.gluLookAt(gl,0f,0f,3f,0f,0f,0f,0f,1f,0f);//设置视点和模型中心位置

    gl.glVertexPointer(3,GL10.GL_FLOAT, 0,cubeBuff);//设置顶点数据
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

    gl.glRotatef(xRot,1f,0f,0f);// 绕着(0,0,0)与(1,0,0)即x轴旋转
    gl.glRotatef(yRot,0f,1f,0f);

    gl.glColor4f(1.0f,0f,0f,1.0f);//设置颜色,红色
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);//绘制正方形FRONT面
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,4,4);

    gl.glColor4f(0f,1.0f,0f,1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,8,4);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,12,4);

    gl.glColor4f(0f,0f,1.0f,1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,16,4);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,20,4);

    xRot += 1.0f;
    yRot += 0.5f;

  }

  /**
   * 将float数组转换为存储在字节缓冲数组
   * @param arr
   * @return
   */
  public FloatBuffer makeFloatBuffer(float[] arr){
    // 分配缓冲空间,一个float占4个字节
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(arr.length*4);
    // 设置字节顺序,其中ByteOrder.nativeOrder()是获取本机字节顺序
    byteBuffer.order(ByteOrder.nativeOrder());
    // 转换为float型
    FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
    // 添加数据
    floatBuffer.put(arr);
    // 设置数组的起始位置
    floatBuffer.position(0);
    return floatBuffer;
  }
}

效果图:

立方体.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容