Android上对OpenGl的支持是无缝的,所以才有众多3D效果如此逼真的游戏,在Camera的一些流程中也有用到GLSurfaceView的情况。本文记录OpenGL在Android上的入门级示例,绘制一个三角形和正方形。尽管功能简单,可是我捣腾了好几个晚上,大量网上文章上的代码都有点问题,不是绘制不出来就是挂了。
第一个文件:MainActivity.java
package com.example.opentest; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GLSurfaceView glSurfaceView = new GLSurfaceView(this); glSurfaceView.setRenderer(new OpenGLRender()); setContentView(glSurfaceView); //setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
第二个文件:BufferUtil.java 负责将数组转成buffer
package com.example.opentest; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; public class BufferUtil { public static FloatBuffer mBuffer; public static FloatBuffer floatToBuffer(float[] a){ //先初始化buffer,数组的长度*4,因为一个float占4个字节 ByteBuffer mbb = ByteBuffer.allocateDirect(a.length*4); //数组排序用nativeOrder mbb.order(ByteOrder.nativeOrder()); mBuffer = mbb.asFloatBuffer(); mBuffer.put(a); mBuffer.position(0); return mBuffer; } public static IntBuffer intToBuffer(int[] a){ IntBuffer intBuffer; //先初始化buffer,数组的长度*4,因为一个float占4个字节 ByteBuffer mbb = ByteBuffer.allocateDirect(a.length*4); //数组排序用nativeOrder mbb.order(ByteOrder.nativeOrder()); intBuffer = mbb.asIntBuffer(); intBuffer.put(a); intBuffer.position(0); return intBuffer; } }
第三个文件:OpenGLRender.java 这是最为核心的,负责给配套的GLSurfaceView绘制东西
package com.example.opentest; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView.Renderer; public class OpenGLRender implements Renderer { private float[] mTriangleArray = { 0f,1f,0f, -1f,-1f,0f, 1f,-1f,0f }; private FloatBuffer mTriangleBuffer; private float[] mColorArray={ 1f,0f,0f,1f, //红 0f,1f,0f,1f, //绿 0f,0f,1f,1f //蓝 }; private FloatBuffer mColorBuffer; //正方形的四个顶点 private FloatBuffer quateBuffer ; private float[] mQuateArray = { -1f, -1f, 0f, 1f, -1f, 0f, -1f, 1f, 0f, 1f, 1f, 0f, }; @Override public void onDrawFrame(GL10 gl) { // TODO Auto-generated method stub gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT); //使用数组作为颜色 gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer); //绘制小三角形 gl.glLoadIdentity(); gl.glTranslatef(-1.5f, 0.0f, -6.0f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mTriangleBuffer);//数组指向三角形顶点buffer gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); // gl.glDisableClientState(GL10.GL_COLOR_ARRAY); gl.glFinish(); //绘制正方形 gl.glLoadIdentity(); gl.glTranslatef(1.5f, 0.0f, -6.0f); // gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, quateBuffer); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); gl.glFinish(); } @Override public void onSurfaceChanged(GL10 gl, int w, int h) { // TODO Auto-generated method stub gl.glViewport(0, 0, w, h); float ratio = (float) w / h; 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 onSurfaceCreated(GL10 gl, EGLConfig config) { // TODO Auto-generated method stub gl.glShadeModel(GL10.GL_SMOOTH); gl.glClearColor(1.0f, 1.0f, 1.0f, 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_NICEST); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); mTriangleBuffer = BufferUtil.floatToBuffer(mTriangleArray); mColorBuffer = BufferUtil.floatToBuffer(mColorArray); quateBuffer = BufferUtil.floatToBuffer(mQuateArray); } }
开发要点:
1、GLSurfaceView可以直接new,也可以放到布局里,本例用的是第一种方法。
2、一个GLSurfaceView要配套一个Renderer,这个Renderer是一个接口,里面有三个函数。这点跟Surfaceview很像。尤其是其中的onDrawFrame()可以类比为Android里View的onDraw()函数。
3、绘制的主题在onDrawFrame()函数里,使用以下代码绘制三角形:
//绘制小三角形 gl.glLoadIdentity(); gl.glTranslatef(-1.5f, 0.0f, -6.0f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mTriangleBuffer);//数组指向三角形顶点 buffergl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); // gl.glDisableClientState(GL10.GL_COLOR_ARRAY); gl.glFinish();
需要注意的是,在一些教程上写着绘制完后要gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 清除所设的顶点,这是个错误!一旦调用此句,则什么画不出来了!!!
然后再绘制正方形:
//绘制正方形 gl.glLoadIdentity(); gl.glTranslatef(1.5f, 0.0f, -6.0f); // gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, quateBuffer); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); gl.glFinish();
注意绘制三角形之前已经加载了Color:gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
如果在绘制完后调用gl.glDisableClientState(GL10.GL_COLOR_ARRAY);的话可以看到三角形一闪而过,正方形都看不到了。这块也是个误解!如果后面通过gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);再次指定颜色,可以看到会以此颜色绘制三角形和正方形。 总而言之,这个onDrawFrame()和View的onDraw()很像,在onDraw里不给paint设颜色,就画。或者画完后,又给颜色设成透明了,结果肯定也是啥都看不到。不明白为啥这么多教程上在绘制完小三角形后非要带:
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
这两句bug!!!
关于代码本身涉及的流程就不解释了,参考链接里说的很清楚。
4、如果正方形四个顶点坐标顺序更换后,画出来的将不是正方形。
源码链接:http://download.csdn.net/detail/yanzi1225627/7484793
效果图如下所示: