2011年9月11日

Android: OpenGL ES 1.0 範例

OpenGL ES Tutorial for Android – Part I – Setting up the view
Yet Another Tutorial on Android 3D Graphics using OpenGL ES Including Nehe's Port:

Android從1.0開始,就支援Open Graphics Library (OpenGL)移動裝置版 OpenGL ES API 用來進行硬體加速2D和3D圖形處理。從Android 2.2 (API Level 8)開始支援OpenGL ES 2.0 API規格(和J2ME JSR239 OpenGL ES API類似)有兩個基本Class讓你方便使用OpenGL ES API。

GLSurfaceView
  • 類似SurfaceView,像畫布一樣在上面作畫,若想接收觸控訊息時,就實做個touch listener,見範例TouchRotateActivity。@Override public boolean onTouchEvent(MotionEvent e) { ........}
GLSurfaceView.Renderer
     此界面實現了在GLSurface上畫畫的動作,經由setRenderer()設定,必須實現方法:
  • onSurfaceCreated(): 當產生GLSurface時呼叫一次。在這裡執行設定OpenGL環境變數,起始  OpenGL圖形物件等動作。 
  • onDrawFrame(): 每一次GLSurface重畫時呼叫此函數。 
  • onSurfaceChanged(): 當GLSurface改變時呼叫此函數,包含大小、方向等。
Android Developer網頁上的OpenGL ES 1.0 的範例
public class OpenGLES10 extends Activity {
    private GLSurfaceView mGLView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 產生一個GLSurfaceView實體,並指定為此Activity的ContentView
        mGLView = new GLES10sv(this);
        setContentView(mGLView);
    }
    @Override
    protected void onPause() {
        super.onPause();
        // 若你的OpenGL程式重度使用記憶體時,
        // 也許可以在此釋放一些物件。
        mGLView.onPause();
    }
    @Override
    protected void onResume() {
        super.onResume();
        // 若你的OpenGL程式在onPause曾釋放記憶體時,
        // 此時可以重新產生物件。
        mGLView.onResume();
    }
}

class GLES10sv extends GLSurfaceView {
    private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
    private GLES10Render mRender;
    private float mPreviousX;
    private float mPreviousY;
    public GLES10sv(Context context){
        super(context);
        // 指定Renderer來作畫
        mRender = new GLES10Render();
        setRenderer(mRender);
        // 當畫面有改變時才畫
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
    @Override 
    public boolean onTouchEvent(MotionEvent e) {
        // MotionEvent reports input details from the touch screen
        // and other input controls. In this case, you are only
        // interested in events where the touch position changed.
        // 觸摸事件回傳的座標並不是和OpenGL座標相同,必須轉換才行。
        float x = e.getX();
        float y = e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:
                float dx = x - mPreviousX;
                float dy = y - mPreviousY;
                // reverse direction of rotation above the mid-line
                if (y > getHeight() / 2) {
                  dx = dx * -1 ;
                }
                // reverse direction of rotation to left of the mid-line
                if (x < getWidth() / 2) {
                  dy = dy * -1 ;
                }
                mRender.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR;
                requestRender();
        }
        mPreviousX = x;
        mPreviousY = y;
        return true;
    } 
}
public class GLES10Render implements GLSurfaceView.Renderer {
    private FloatBuffer triangleVB;
    public float mAngle;
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //設定背景顏色
        gl.glClearColor(0.0f,0.5f,0.0f,1.0f);
        //起始三角形陣列
        initShapes();
        //使用點陣列
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    }
    public void onDrawFrame(GL10 gl) {
        //重畫背景顏色
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        gl.glMatrixMode(GL10.GL_MODELVIEW);// 設成GL_MODELVIEW轉換模式
        gl.glLoadIdentity(); //重置成I矩陣
        //當使用GL_MODELVIEW,必須設置視點(view point
        GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);        
         //旋轉mAngle
        gl.glRotatef(mAngle, 0.0f, 0.0f, 1.0f); 
        //畫三角形
        gl.glColor4f(1.0f, 0.0f, 0.0f, 0.0f);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleVB);
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
    }
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);        
        float ratio = (float)width/height; //調整螢幕比例
        gl.glMatrixMode(GL10.GL_PROJECTION);//設成投影模式
        gl.glLoadIdentity(); //重置成I矩陣
        gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);//使用此投影矩陣
    }
}

預設OpenGL ES將[0,0,0] (X,Y,Z)為GLSurfaceView畫面的中心,[1,1,0] 右上角,而[-1,-1,0]為畫面左下角

  private void initShapes(){   //定義一個平面上的正三角形
     float triangleCoords[] = {
         // X, Y, Z
         -0.5f, -0.25f, 0,
          0.5f, -0.25f, 0,
          0.0f,  0.559016994f, 0
     }; 
     // initialize vertex Buffer for triangle  
     //(# of coordinate values * 4 每一個float佔4個byte)
     ByteBuffer vbb=ByteBuffer.allocateDirect(triangleCoords.length*4); 
     // 
     vbb.order(ByteOrder.nativeOrder()); //使用裝置硬體原生的位元組順序
     triangleVB = vbb.asFloatBuffer();   //衍生floating point buffer 
     triangleVB.put(triangleCoords);     //放入座標
     triangleVB.position(0);             //指向第一個座標位置
 }

把initShapes放入onSurfaceCreated()作起始動作,切記這些圖形物件的啟始動作千萬不要放在onDrawFrame()。


沒有留言: