Android上對OpenGl的支持是無縫的,所以才有眾多3D效果如此逼真的游戲,在Camera的一些流程中也有用到GLSurfaceView的情況。本文記錄OpenGL在Android上的入門級示例,繪制一個三角形和正方形。盡管功能簡單,可是我搗騰了好幾個晚上,大量網上文章上的代碼都有點問題,不是繪制不出來就是掛了。
第一個文件:MainActivity.Java
- packagecom.example.learnopengl1;
-
- importandroid.opengl.GLSurfaceView;
- importandroid.os.Bundle;
- importandroid.app.Activity;
- importandroid.view.Menu;
-
- publicclassMainActivityextendsActivity{
-
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- GLSurfaceViewglSurfaceView=newGLSurfaceView(this);
- glSurfaceView.setRenderer(newOpenGLRender());
- setContentView(glSurfaceView);
- //setContentView(R.layout.activity_main);
-
- }
-
- @Override
- publicbooleanonCreateOptionsMenu(Menumenu){
- //Inflatethemenu;thisaddsitemstotheactionbarifitispresent.
- getMenuInflater().inflate(R.menu.main,menu);
- returntrue;
- }
-
- }
-
第二個文件:BufferUtil.java 負責將數組轉成buffer
- packagecom.example.learnopengl1;
-
- importjava.nio.ByteBuffer;
- importjava.nio.ByteOrder;
- importjava.nio.FloatBuffer;
- importjava.nio.IntBuffer;
-
- publicclassBufferUtil{
- publicstaticFloatBuffermBuffer;
- publicstaticFloatBufferfloatToBuffer(float[]a){
- //先初始化buffer,數組的長度*4,因為一個float占4個字節
- ByteBuffermbb=ByteBuffer.allocateDirect(a.length*4);
- //數組排序用nativeOrder
- mbb.order(ByteOrder.nativeOrder());
- mBuffer=mbb.asFloatBuffer();
- mBuffer.put(a);
- mBuffer.position(0);
- returnmBuffer;
- }
-
- publicstaticIntBufferintToBuffer(int[]a){
-
- IntBufferintBuffer;
- //先初始化buffer,數組的長度*4,因為一個float占4個字節
- ByteBuffermbb=ByteBuffer.allocateDirect(a.length*4);
- //數組排序用nativeOrder
- mbb.order(ByteOrder.nativeOrder());
- intBuffer=mbb.asIntBuffer();
- intBuffer.put(a);
- intBuffer.position(0);
- returnintBuffer;
- }
- }
第三個文件:OpenGLRender.java 這是最為核心的,負責給配套的GLSurfaceView繪制東西
- packagecom.example.learnopengl1;
-
- importjava.nio.FloatBuffer;
- importjavax.microedition.khronos.egl.EGLConfig;
- importjavax.microedition.khronos.opengles.GL10;
- importandroid.opengl.GLSurfaceView.Renderer;
-
- publicclassOpenGLRenderimplementsRenderer{
-
- privatefloat[]mTriangleArray={
- 0f,1f,0f,
- -1f,-1f,0f,
- 1f,-1f,0f
- };
- privateFloatBuffermTriangleBuffer;
-
-
- privatefloat[]mColorArray={
- 1f,0f,0f,1f,//紅
- 0f,1f,0f,1f,//綠
- 0f,0f,1f,1f//藍
- };
- privateFloatBuffermColorBuffer;
-
- //正方形的四個頂點
- privateFloatBufferquateBuffer;
- privatefloat[]mQuateArray={
- -1f,-1f,0f,
- 1f,-1f,0f,
- -1f,1f,0f,
- 1f,1f,0f,
- };
-
- @Override
- publicvoidonDrawFrame(GL10gl){
- //TODOAuto-generatedmethodstub
- 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
- publicvoidonSurfaceChanged(GL10gl,intw,inth){
- //TODOAuto-generatedmethodstub
- gl.glViewport(0,0,w,h);
-
- floatratio=(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
- publicvoidonSurfaceCreated(GL10gl,EGLConfigconfig){
- //TODOAuto-generatedmethodstub
- 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);//數組指向三角形頂點buffer
gl.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、如果正方形四個頂點坐標順序更換後,畫出來的將不是正方形。
效果圖如下所示: