安卓平臺OpenGL ES的調用

安卓平臺OpenGL ES的調用

  • 開發遊戲如果直接使用OpenGL是比較痛苦的,最好的辦法是使用封裝好的引擎,但很有必要了解在安卓java代碼直接調用OpenGL的渲染方法
  • 固定渲染管線只可通過配置實現不同的效果,而可編程渲染管線通過一般編程的方式實現,可以實現更加靈活的效果

OpenGL ES

  • OpenGL從3.0開始,而OpenGL ES從2.0開始,支持可編程管線
  • 下圖中橙色兩塊兒即爲可編程部分,而頂點和片段着色器要相互配合好才能發揮最大性能

  • 一般通用編程模式爲寫好兩個shader的代碼,每次運行程序時即時編譯運行

代碼框架

  • 在manifest中聲明OpenGL ES <uses-feature android:glEsVersion="0x00020000" android:required="true" />
  • 在入口Acitvity中需要使用GLSurfaceView作爲主視圖
  • 在GLSurfaceView中可以擴展出一些其他函數,需要創建一個GLSurfaceView.Renderer的派生類對象負責顯示
  • GLSurfaceView.Renderer需要關注三個函數:
    • onSurfaceCreated:僅調用一次
    • onDrawFrame:每次顯示都調用
    • onSurfaceChanged:view大小變化時調用

3D座標變換

  • 安卓同樣有對應座標變換的庫Matrix
  • MVP變換的實際乘法順序是PVM
  • V可以通過setLookAtM得到
  • P則是orthoM frustumM/perspectiveM生成,其中perspectiveM是對frustumM的封裝,只是在API Level 14後才存在
  • M如果不是I的話,表明模型有位置偏移

使用shader畫圖

  • 需要創建一個頂點shader、一個片段shader,以及一個program
  • 通過glCreateShader可以創建一個shader,之後調用glShaderSourceglCompileShader分別設置shader代碼和編譯
  • progam則通過glCreateProgram創建,調用glAttachShader添加這兩個shader,調用glLinkProgram進行“鏈接”生成可執行指令,使用時要glUseProgram

示例代碼

  • OpenGLES20Activity.java

    package com.example.androiddeveloper;
    
    //import android.support.v7.app.ActionBarActivity;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.MotionEvent;
    
    import javax.microedition.khronos.opengles.GL10;
    
    import android.app.Activity;
    import android.content.Context;
    import android.opengl.*;
    
    public class OpenGLES20Activity extends Activity {
    
        private GLSurfaceView mGLView;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // Create a GLSurfaceView instance and set it
            // as the ContentView for this Activity.
            mGLView = new MyGLSurfaceView(this);
            setContentView(mGLView);
        }
    }
    
    class MyGLSurfaceView extends GLSurfaceView {
    
        private final MyGLRenderer mRenderer;
    
        public MyGLSurfaceView(Context context){
            super(context);
    
            // Create an OpenGL ES 2.0 context
            setEGLContextClientVersion(2);
            //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    
            mRenderer = new MyGLRenderer();
    
            // Set the Renderer for drawing on the GLSurfaceView
            setRenderer(mRenderer);
        }
    
        private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
        private float mPreviousX;
        private float mPreviousY;
    
        @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.
    
            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 ;
                    }
    
                    mRenderer.setAngle(
                            mRenderer.getAngle() +
                            ((dx + dy) * TOUCH_SCALE_FACTOR));
                    requestRender();
            }
    
            mPreviousX = x;
            mPreviousY = y;
            return true;
        }
    }
    
  • MyGLRenderer.java

    package com.example.androiddeveloper;
    
    import javax.microedition.khronos.opengles.GL10;
    
    import android.opengl.GLES20;
    import android.opengl.GLSurfaceView;
    import android.opengl.Matrix;
    import android.os.SystemClock;
    
    public class MyGLRenderer implements GLSurfaceView.Renderer {
    
        private Triangle mTriangle;
    
        private final float[] mMVPMatrix = new float[16];
        private final float[] mProjectionMatrix = new float[16];
        private final float[] mViewMatrix = new float[16];
    
    
        public void onSurfaceCreated(GL10 unused, javax.microedition.khronos.egl.EGLConfig config) {
            // Set the background frame color
            GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
            mTriangle = new Triangle();
        }
    
        private float[] mRotationMatrix = new float[16];
        public void onDrawFrame(GL10 unused) {
            float[] scratch = new float[16];
            long time = SystemClock.uptimeMillis() % 4000L;
            float angle = 0.090f * ((int) time);
            Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
    
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    
            Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
            Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
    
    
            Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
    
            mTriangle.draw(scratch);
        }
    
        public void onSurfaceChanged(GL10 unused, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
    
    
    
            float ratio = (float) width / height;
            Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        }
    
        public static int loadShader(int type, String shaderCode){
    
            // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
            // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
            int shader = GLES20.glCreateShader(type);
    
            // add the source code to the shader and compile it
            GLES20.glShaderSource(shader, shaderCode);
            GLES20.glCompileShader(shader);
    
            return shader;
        }
    
        public volatile float mAngle;
    
        public float getAngle() {
            return mAngle;
        }
    
        public void setAngle(float angle) {
            mAngle = angle;
        }
    }
    
  • Triangle.java

    package com.example.androiddeveloper;
    
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.nio.FloatBuffer;
    
    import android.opengl.GLES20;
    
    public class Triangle {
    
        private FloatBuffer vertexBuffer;
    
        // number of coordinates per vertex in this array
        static final int COORDS_PER_VERTEX = 3;
        static float triangleCoords[] = {   // in counterclockwise order:
                 0.0f,  0.622008459f, 0.0f, // top
                -0.5f, -0.311004243f, 0.0f, // bottom left
                 0.5f, -0.311004243f, 0.0f  // bottom right
        };
    
        // Set color with red, green, blue and alpha (opacity) values
        float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
    
        private final int mProgram;
    
    
        public Triangle() {
            // initialize vertex byte buffer for shape coordinates
            ByteBuffer bb = ByteBuffer.allocateDirect(
                    // (number of coordinate values * 4 bytes per float)
                    triangleCoords.length * 4);
            // use the device hardware's native byte order
            bb.order(ByteOrder.nativeOrder());
    
            // create a floating point buffer from the ByteBuffer
            vertexBuffer = bb.asFloatBuffer();
            // add the coordinates to the FloatBuffer
            vertexBuffer.put(triangleCoords);
            // set the buffer to read the first coordinate
            vertexBuffer.position(0);
    
            int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                    vertexShaderCode);
            int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                            fragmentShaderCode);
    
            // create empty OpenGL ES Program
            mProgram = GLES20.glCreateProgram();
    
            // add the vertex shader to program
            GLES20.glAttachShader(mProgram, vertexShader);
    
            // add the fragment shader to program
            GLES20.glAttachShader(mProgram, fragmentShader);
    
            // creates OpenGL ES program executables
            GLES20.glLinkProgram(mProgram);
        }
    
        private final String vertexShaderCode =
            "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "void main() {" +
            "  gl_Position = uMVPMatrix * vPosition;" +
            "}";
    
        private final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "  gl_FragColor = vColor;" +
            "}";
    
        private int mPositionHandle;
        private int mColorHandle;
    
        private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
        private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
    
        private int mMVPMatrixHandle;
    
        public void draw(float[] mvpMatrix) {
            // Add program to OpenGL ES environment
            GLES20.glUseProgram(mProgram);
    
            // get handle to vertex shader's vPosition member
            mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    
            // Enable a handle to the triangle vertices
            GLES20.glEnableVertexAttribArray(mPositionHandle);
    
            // Prepare the triangle coordinate data
            GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                         GLES20.GL_FLOAT, false,
                                         vertexStride, vertexBuffer);
    
            // get handle to fragment shader's vColor member
            mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
    
            // Set color for drawing the triangle
            GLES20.glUniform4fv(mColorHandle, 1, color, 0);
    
            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
            GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
    
            // Draw the triangle
            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
    
            // Disable vertex array
            GLES20.glDisableVertexAttribArray(mPositionHandle);
        }
    }
    

作者Focustc,來自於CSDN
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章