使用Android OpenGL ES 2.0繪圖之三:繪製形狀

傳送門 ☞ 輪子的專欄 ☞ 轉載請註明 ☞ http://blog.csdn.net/leverage_1229

         在定義好待繪製的形狀之後,就要開始繪製它們了。使用OpenGL ES 2.0繪製形狀可能比你想象的要複雜一些,因爲它的API中提供了大量控制渲染管道的行爲。

        這一節將介紹如何使用OpenGL ES 2.0的API繪製上一節你已經定義好的那些形狀。

1初始化形狀

        在做任何繪製之前,你必須初始化形狀並加載它。除非形狀的結構(原始座標)在程序執行過程中發生了改變,都應該在你的Renderer類的onSurfaceCreated()方法中進行初始化內存和處理效率。
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    ...

    // 初始化一個三角形
    mTriangle = new Triangle();
    // 初始化一個正方形
    mSquare = new Square();
}

2繪製一個形狀

        使用OpenGL ES 2.0繪製一個定義好的形狀需要大量的代碼,因爲必須提供給圖形渲染管道很多細節信息。具體定義如下:
2.1VertexShader:用於呈現形狀頂點的OpenGL ES圖形代碼。
2.2FragmentShader:用於呈現形狀外觀(顏色或紋理)的OpenGL ES代碼。
2.3Program:一個OpenGL ES對象,包含了你想要用來繪製一個或多個形狀的shader。
        你至少需要一個vertex shader來繪製一個形狀和一個fragment shader來爲形狀着色。這些shader必須被編譯,然後將它們添加到一個OpenGL ES program中,接着使用program繪製形狀。這裏有一個例子,演示瞭如何定義基本的shader來繪製一個形狀:
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。可以在你的renderer對象的構造器中做這些事情,因爲只需要執行一次。
        注:編譯OpenGL ES shader和鏈接program是很耗CPU的,所以你應該儘量避免多次執行它們。如果在運行時還不知道shader的內容,那麼就應該只創建一次代碼,然後緩存起來以備後用。

public Triangle() {
    ...

    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();             // 創建空的OpenGL ES Program
    GLES20.glAttachShader(mProgram, vertexShader);   // 將vertex shader添加到program
    GLES20.glAttachShader(mProgram, fragmentShader); // 將fragment shader添加到program
    GLES20.glLinkProgram(mProgram);                  // 創建可執行的 OpenGL ES program
}

        此時,應該可以添加真正的繪製調用了。你需要提供一些參數給渲染管道,告訴它想要繪製什麼以及如何繪製。因爲繪製操作是因形狀而異的,讓形狀類包含特定的繪製邏輯是一個不錯的設計。
        創建一個draw()方法負責繪製形狀。下面的代碼將展示向形狀的vertex shader和fragment shader設置位置和顏色值,然後執行繪製操作。

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);

    // 繪製三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

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

        一旦你完成了這些代碼,繪製這個對象只需要在Renderer類的onDrawFrame()方法中調用draw()方法。當你運行應用程序時,它看起來應該是這樣的:

 

        此示例中的代碼還存在很多問題。首先,它不會給人留下深刻印象。其次,當你改變設備屏幕的方向時,三角形會被壓扁和改變形狀。原因是它的各個頂點無法根據屏幕的寬高比例進行修正。可以使用投影和相機視圖來解決這個問題。不過那是下一節的內容了。
        最後,三角形是靜止不動的,這樣看起來有點無聊。在後續內容中,我們還將介紹如何使用OpenGL ES圖形管道讓形狀旋轉起來,以及更多有趣的用法。

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