OpenGL(全寫Open Graphics Library)是一個跨語言、跨平台的三維圖象編程介面,同樣他也可以用來建立二維映像。OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL三維圖形 API 的子集,針對手機、PDA和遊戲主機等嵌入式裝置而設計。android 平台上同樣整合了opengl es的開發包,opengl es在android平台上的運用,既有利於充分利用android平台不斷進步的硬體設定,也能為使用者提供更加多姿多彩的視覺體驗。android平台建立三維動畫有二種方式,一種是使用matrix三維變形實現偽3D,一種就是使用opengl建立真3D。使用matrix三維變形,方法簡單,速度快,效率高,但因為其是偽3D,所以拋開視覺感受不談,其缺陷也非常明顯,他沒有真正的3D建模,只是對二維的VIEW做3D移動,大小變換和旋轉等操作,當你需要建立一個旋轉的六面體時,你需要建立六個VIEW,並讓他們實現繞一個虛擬軸做同步旋轉,當這個3D模型有足夠多的面時,其複雜度大大增加了,因為在初始化時需要提前計算每個面的初始位置資訊,旋轉,大小,位移的同步資訊,這對於非矩形的view來說,這種計算相當繁瑣而且易於出錯並且不容易精確,而且目前來看,使用偽3D技術構建規則曲面似乎是難以實現。這時opengl es的優勢相當明顯。你可以建立任意形狀的多面體,只要把VIEW轉為BITMAP為多面體貼圖就行了。android使用opengl ES建立立方體,需要用到下面一些函數。gl.glFrontFace(GL10.GL_CW); gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer); gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer); void glFrontFace(GLenum mode);作用是控制多面形的正面是如何決定的。在預設情況下,mode是GL_CCW。一個多邊形有兩個面,每個面使用不同的介質貼圖,旋轉時是可以看到mode的值為:GL_CCW 表示視窗座標上投影多邊形的頂點順序為逆時針方向的表面為正面。GL_CW 表示頂點順序為順時針方向的表面為正面。void glVertexPointer(GLint size,GLenum type,GLsizei stride,const GLvoid * pointer) 作用是指定多面體每個頂點的座標, size:指定了每個頂點對應的座標個數,只能是2,3,4中的一個,預設值是4type:指定了數組中每個頂點座標的資料類型,可取常量:GL_BYTE, GL_SHORT,GL_FIXED,GL_FLOAT;stride:指定了連續頂點間的位元組相片順序,如果為0,數組中的頂點就會被認為是按照緊湊方式排列的,預設值為0pointer:制訂了數組中第一個頂點的首地址,預設值為0,對於我們的android,大家可以不用去管什麼地址的,一般給一個IntBuffer就可以了。需要說明的是第二個參數,一般在android中,會使用 GL10.GL_FIXED和GL10. FLOAT, 整型和浮點型相互對應, 0x10000=1.0f,整型的低八位表示浮點的小數,高八位表示浮點小數部分。而且第四個參數必須是 nativeOrder,如果是直接賦值的數組,需要使用下面代碼轉化 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asIntBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); gl.glColorPointer這個是顏色了,和上一個函數一樣,是指定每個頂點的顏色值,參數結構也類似 gl.glDrawElements這個是用來顯示的,六面體六個面,有12個三角形,每個三角形三個頂點,共36個 這樣就可以得到下面的四面體的類:package com.example.openglactivity; import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import java.nio.IntBuffer; import javax.microedition.khronos.opengles.GL10; import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLUtils; public class Cube { private IntBuffer mVertexBuffer; private IntBuffer mColorBuffer; private ByteBuffer mIndexBuffer; // 定義本程式所使用的紋理 private int texture; public Cube() { int one = 0x10000; //int 10000=1.0f int vertices[] = { -one, -one, -2000,//-one, //第三象限 one, -one, -2000,//-one, //第四象限 one, one, -2000,//-one, //第一象限 -one, one, -2000,//-one, //第二象限 -one, -one, 0, //one, //第三象限 one, -one, 0, //one, //第四象限 one, one, 0, //one, //第一象限 -one, one, 0, //one, //第二象限 }; 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, }; 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 }; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asIntBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4); cbb.order(ByteOrder.nativeOrder()); mColorBuffer = cbb.asIntBuffer(); mColorBuffer.put(colors); mColorBuffer.position(0); mIndexBuffer = ByteBuffer.allocateDirect(indices.length); mIndexBuffer.put(indices); mIndexBuffer.position(0); } public void draw(GL10 gl) { gl.glFrontFace(GL10.GL_CW); gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer); gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer); } } 這是使用opengl建立的體面體,要匯入android中使用,還需要經過一些步驟,一個是GLSurfaceView,一個是Renderer,Renderer是GLSurfaceView提供的介面,我們需要通過重載Renderer載入我們的四面體,然後通過setRenderer把包含我們六面體Renderer傳給GLSurfaceView,最後通過activity的setContentView把GLSurfaceView設定為activity的內容,代碼如下: myGLRenderer 派生自Renderer,需要實現 Renderer的 onSurfaceCreated, onSurfaceChanged, onDrawFrame三個方法。這是比較重要的,定義了顯示3D模型的環境,顯示方式顯示特效等一系列東西。 onSurfaceCreated和 onSurfaceChanged熟悉surfaceview的人肯定不陌生,他原本就是surfaceview的方法,我們對於3D螢幕初始化,opengl環境設定都會在這兩個函數裡實現,然後就是onDrawFrame,用來顯示,大致來說,應該和view的onDraw差不多的功能,能夠自動適時重新整理。用到的函數是這幾個:gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 設定背景顏色gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 啟動頂點數組支援,類似的函數還有gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);一個啟動法向量支援,一個啟動紋理渲染支援 gl.glViewport(0, 0, width, height);設定顯示地區,一般就是整個螢幕吧 gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); 這兩句是設定當前矩陣模式和重載矩陣,不可省略,有三種模式,GL_MODELVIEW,對模型視景矩陣堆棧應用隨後的矩陣操作.GL_PROJECTION,對投影矩陣應用隨後的矩陣操作.GL_TEXTURE,對紋理矩陣堆棧應用隨後的矩陣操作.設定矩陣後必須使用 glLoadIdentity才會生效,該函數的功能是重設當前指定的矩陣為設定矩陣。還有一個函數gluPerspective也很重要,他是建立一個投影矩陣並且與當前矩陣相乘,得到的矩陣設定為當前變換,但要先通過glMatrixMode設定成GL_PROJECTION 投影矩陣才會得到想要的投影矩陣變換。 gl.glFrustumf(-ratio, ratio, -1, 1, 3, 100); ratio=(float) width / height,所以 glFrustumf是設定長寬比例,因為opengl是按預設的正方形螢幕投影的,這樣當在一個高比寬大的長方形的屏上時,投影的正方形會變成高比寬大的長方形,要真實的投影,需要通過這個函數來校正投投影。GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 建立觀察者座標系,這裡可以參考下面的文章:http://www.cnblogs.com/chengmin/archive/2011/09/12/2174004.htmlgl.glRotatef(ang, 0f, 0.2f, 0f); 這個是對函數進行旋轉,第一個參數是角度,後面三個參數是XYZ三個方向,類似的函數還有gl.glTranslatef(0, 0, -3.5f);移動操作package com.example.openglactivity;import java.nio.Buffer;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import java.nio.IntBuffer; import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView.Renderer;import android.opengl.GLU; public class myGLRenderer implements Renderer { private Cube m_cube; private float ang = 0.0f; public myGLRenderer() { m_cube = new Cube(); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // TODO Auto-generated method stub gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //設定清除色 } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { // TODO Auto-generated method stub gl.glViewport(0, 0, width, height);//設定視口 float ratio = (float) width / height; gl.glMatrixMode(GL10.GL_PROJECTION); // 設定當前矩陣為投影矩陣 gl.glLoadIdentity(); // 重設矩陣為初始值 gl.glFrustumf(-ratio, ratio, -1, 1, 3, 100); // 根據長寬比設定投影矩陣 } @Override public void onDrawFrame(GL10 gl) { // TODO Auto-generated method stub gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);//清空緩衝 // 設定當前矩陣為模型視圖模式 // gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); // reset the matrix to its default state gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); // 設定視點 GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); gl.glRotatef(ang, 0f, 0.2f, 0f); m_cube.draw(gl); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisableClientState(GL10.GL_COLOR_ARRAY); ang+=1.0f; }} myGLSurfaceView類派生自 GLSurfaceView,GLSurfaceView 派生自SurfaceView,這下一切就熟悉了,SurfaceView是為了動畫,特效而設計的高效UI類。 GLSurfaceView是專門為opengl服務的類。這裡也十分簡單。 setRenderer就行了,可以查看setRenderer在 GLSurfaceView裡的實現: public void setRenderer(Renderer renderer) { checkRenderThreadState(); if (mEGLConfigChooser == null) { mEGLConfigChooser = new SimpleEGLConfigChooser(true); } if (mEGLContextFactory == null) { mEGLContextFactory = new DefaultContextFactory(); } if (mEGLWindowSurfaceFactory == null) { mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); } mRenderer = renderer; mGLThread = new GLThread(renderer); mGLThread.start(); }開了個新線程去操作 renderer。下面是 myGLSurfaceView代碼 package com.example.openglactivity; import android.content.Context;import android.graphics.PixelFormat;import android.opengl.GLSurfaceView;import android.view.KeyEvent; public class myGLSurfaceView extends GLSurfaceView { private myGLRenderer mrender; public myGLSurfaceView(Context context) { super(context); // TODO Auto-generated constructor stub //getHolder().setFormat(PixelFormat.TRANSLUCENT); mrender = new myGLRenderer(); setRenderer(mrender); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // TODO Auto-generated method stub return super.onKeyDown(keyCode, event); }} 最後看一下 Activity的調用,一切都瞭解了,和調用普通的view沒有區別。package com.example.openglactivity; import android.os.Bundle;import android.app.Activity;import android.view.Menu; public class MainActivity extends Activity { private myGLSurfaceView mGLSurfaceView; public static Activity instance = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); instance = this; mGLSurfaceView = new myGLSurfaceView(this); setContentView(mGLSurfaceView);//這裡我們用mGLSurfaceView來替換以前常用的R.layout.main } @Override protected void onDestroy() { // TODO Auto-generated method stub instance = null; super.onDestroy(); } @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; } }