OpenGL ES2.0入門之Android篇(一)——繪製三角形

轉載請註明出處: http://blog.csdn.net/lb377463323/article/details/52136518

OpenGL ES簡介

  • OpenGL ES是一個爲便攜式或嵌入式設備例如:移動電話、監視器等發展的3D繪圖API。
  • 在Android框架中有兩個基礎類用於使用OpenGL ES創建和處理圖形
    • GLSurfaceView類是OpenGL ES繪製圖形的view容器
    • GLSurfaceView.Renderer類是用於控制上述view容器中顯示什麼內容

Android使用OpenGL ES繪製三角形

  • 整個過程比較繁瑣,代碼也挺多的。因爲 OpenGL ES 2.0 是以可編程着色器爲基礎的,這意味着你繪製任何圖形都必須有一
    個合適的着色器裝載和綁定,比使用固定管線的桌面版本有更多代碼。

1. 在manifest中聲明使用OpenGL ES 2.0 API

<uses-feature android:glEsVersion="0x00020000" android:required="true" />

0x00020000代表OpenGL ES 2.0版本,3.0版本是0x00030000,3.1版本是0x00030001

  • 若應用需要使用紋理壓縮(紋理壓縮通過減少內存需求和更有效的使用內存帶寬使OpenGL ES應用的性能顯著增加)功能,還需在manifest聲明所支持的壓縮格式,如下:


2. 將Activity的ContentView設爲GLSurfaceView

    public class OpenGLES20Activity extends Activity {

    private GLSurfaceView mGLView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGLView = new MyGLSurfaceView(this);
        setContentView(mGLView);
        }
    }

3. 構造GLSurfaceView對象

class MyGLSurfaceView extends GLSurfaceView {

    private final MyGLRenderer mRenderer;

    public MyGLSurfaceView(Context context){
        super(context);

        // 創建OpenGL ES 2.0的上下文
        setEGLContextClientVersion(2);

        mRenderer = new MyGLRenderer();

        //設置Renderer用於繪圖
        setRenderer(mRenderer);

        //只有在繪製數據改變時才繪製view,可以防止GLSurfaceView幀重繪
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
}

4. 構造GLSurfaceView.Renderer對象

  • Renderer類提供三個回調方法供Android系統調用,用來計算在GLSurfaceView中繪製什麼以及如何繪製。

    • onSurfaceCreated():僅調用一次,用於設置view的OpenGL ES環境
    • onDrawFrame():每次重繪view時調用
    • onSurfaceChanged():當view的幾何形狀發生變化時調用,比如設備屏幕方向改變時

    public class MyGLRenderer implements GLSurfaceView.Renderer {

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    //設置背景色(r,g,b,a)
    GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);//白色不透明
    }

    public void onDrawFrame(GL10 unused) {
    //重繪背景色
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }

    public void onSurfaceChanged(GL10 unused, int width, int height) {
    //繪製窗口
    GLES20.glViewport(0, 0, width, height);
    }
    }

5. 在定義三角形之前需瞭解Android和OpenGL ES的座標系

  • Android屏幕左上角爲原點,往右爲X正方向,往下爲Y正方向
  • Android屏幕的中心點座標爲OpenGL的原點,方向如下圖,並且最大和最小值爲1和-1
  • 也就是說OpenGL的(-1,1,0)爲屏幕的左上角,

6. 定義一個三角形

public class Triangle {

    private FloatBuffer vertexBuffer;

    //設置每個頂點的座標數
    static final int COORDS_PER_VERTEX = 3;
    //設置三角形頂點數組
    static float triangleCoords[] = {   //默認按逆時針方向繪製
         0.0f,  1.0f, 0.0f, // 頂點
        -1.0f, -0.0f, 0.0f, // 左下角
         1.0f, -0.0f, 0.0f  // 右下角
};

// 設置三角形顏色和透明度(r,g,b,a)
float color[] = {0.0f, 1.0f, 0f, 1.0f};//綠色不透明

public Triangle() {
    // 初始化頂點字節緩衝區,用於存放形狀的座標
    ByteBuffer bb = ByteBuffer.allocateDirect(
            //(每個浮點數佔用4個字節
            triangleCoords.length * 4);
    //設置使用設備硬件的原生字節序
    bb.order(ByteOrder.nativeOrder());

    //從ByteBuffer中創建一個浮點緩衝區
    vertexBuffer = bb.asFloatBuffer();
    // 把座標都添加到FloatBuffer中
    vertexBuffer.put(triangleCoords);
    //設置buffer從第一個座標開始讀 
    vertexBuffer.position(0);
    }
}

7. 在MyGLRenderer中初始化三角形形狀

private Triangle mTriangle;
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    ...
    //初始化三角形
    mTriangle = new Triangle();
}

8. 繪製三角形形狀

使用OpenGL ES 2.0繪製一個定義好的形狀需要大量的代碼,因爲必須提供給圖形渲染管道很多細節信息

  • VertexShader:用於渲染形狀的頂點的OpenGL ES圖形代碼
  • FragmentShader:用於渲染形狀的外觀(顏色或紋理)的OpenGL ES代碼
  • Program:一個OpenGL ES對象,包含了你想要用來繪製一個或多個形狀的shader

至少需要一個vertex shader來繪製一個形狀和一個fragment shader來爲形狀着色

public class Triangle {

    private final String vertexShaderCode =
        "attribute vec4 vPosition;" +
        "void main() {" +
        "  gl_Position = vPosition;" +
        "}";

    private final String fragmentShaderCode =
        "precision mediump float;" +
        "uniform vec4 vColor;" +
        "void main() {" +
        "  gl_FragColor = vColor;" +
        "}";

    ...
}

Shader包含OpenGL Shading Language(GLSL)代碼,必須在OpenGL ES環境下先編譯再使用。想要編譯這些代碼,需要在你的Renderer類中創建一個工具類方法:

public static int loadShader(int type, String shaderCode){

    //創建一個vertex shader類型(GLES20.GL_VERTEX_SHADER)
    //或一個fragment shader類型(GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);

    // 將源碼添加到shader並編譯它
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}

在三角形類中編譯shader代碼,並將它們添加到一個OpenGL ES program 對象中,然後鏈接這個program

public class Triangle() {
...

private final int mProgram;

public Triangle() {
    ...
    // 編譯shader代碼
    int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                                    vertexShaderCode);
    int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                                    fragmentShaderCode);

    // 創建空的OpenGL ES Program
    mProgram = GLES20.glCreateProgram();

    // 將vertex shader添加到program
    GLES20.glAttachShader(mProgram, vertexShader);

    // 將fragment shader添加到program
    GLES20.glAttachShader(mProgram, fragmentShader);

    // 創建可執行的 OpenGL ES program  
    GLES20.glLinkProgram(mProgram);
    }
}

在三角形類中創建一個draw()方法負責繪製形狀

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

public void draw() {
    // 添加program到OpenGL ES環境中
    GLES20.glUseProgram(mProgram);

    // 獲取指向vertex shader的成員vPosition的handle 
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // 啓用一個指向三角形的頂點數組的handle
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    //準備三角形的座標數據
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);

    // 獲取指向fragment shader的成員vColor的handle
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    //  繪製三角形
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // 禁用指向三角形的頂點數組 
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}

9. 在Render類中的onDrawFrame()方法中調用draw()方法

public void onDrawFrame(GL10 unused) {
    ...

    mTriangle.draw();
}

此時運行程序顯示如下圖:

到這裏,三角形已繪製完畢,下一篇介紹添加動作及觸摸事件。

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