Android OpenGL ES 2.0(一)---入門

本文從下面鏈接翻譯過來:

Android Lesson One: Getting Started

這是在Android上使用OpenGL ES2的第一個教程。 在本課中,我們將逐步介紹代碼,並瞭解如何創建OpenGL ES2上下文並繪製到屏幕上。 我們還將瞭解着色器是什麼以及它們如何工作,以及如何使用矩陣將場景轉換爲您在屏幕上看到的圖像。 最後,您需要在清單文件(AndroidManifest.xml)中添加使用OpenGL ES2的說明,以告知Android應用市場你的應用僅對支持的設備可見。

開發環境搭建

在開始之前,您需要確保在計算機上安裝了以下工具:
1.Java環境
2.Android studio開發工具
3.Android真機設備一部

開始

我們將查看下面的所有代碼並解釋每個部分的作用。 您可以通過創建自己的項目逐段複製代碼,也可以在課程的最後下載完整的項目代碼。 安裝完工具後,在Android Studio中創建一個新的Android項目。 名稱無關緊要,但對於本課,我將應用的入口稱爲LessonOneActivity。

我們來看看代碼:

    /** GLSurfaceView的一個引用 */
    private GLSurfaceView mGLSurfaceView;

GLSurfaceView是一個特殊的視圖,它爲我們管理OpenGL表面並將其繪製到Android視圖系統中。 它還增加了許多功能,使其更易於使用OpenGL,包括但不限於:

  • 它爲OpenGL提供專用的渲染線程,以便不影響到Android主線程。
  • 它支持連續或按需渲染。
  • 它使用EGL(OpenGL和底層窗口系統之間的接口)來處理屏幕設置。

GLSurfaceView使得從Android設置和使用OpenGL相對輕鬆。

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mGLSurfaceView = new GLSurfaceView(this);
        // 檢查系統是否支持 OpenGL ES 2.0.
        final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
        final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
        if (supportsEs2) {
            // 請求使用OpenGL ES 2.0兼容的上下文.
            mGLSurfaceView.setEGLContextClientVersion(2);
            // 將渲染器設置爲我們的演示渲染器,定義如下
            mGLSurfaceView.setRenderer(new LessonOneRenderer());
        } else {
            //如果您想同時支持ES 1和ES 2,則可以在此處創建與OpenGL ES 1.x兼容的渲染器。
            return;
        }
        setContentView(mGLSurfaceView);
    }

LessonOneActivity的onCreate()方法是創建OpenGL上下文以及一切開始重要部分。在onCreate()中,在調用父類之後做的第一件事是創建我們的GLSurfaceView。然後我們需要弄清楚系統是否支持OpenGL ES2。爲此,我們得到一個ActivityManager實例,它允許我們與全局系統狀態進行交互。然後我們可以使用它來獲取設備配置信息,它將告訴我們設備是否支持OpenGL ES2。

一旦我們知道設備支持OpenGL ES2,我們告訴GLSurfaceView我們想要一個OpenGL ES2兼容表面,然後我們傳入一個自定義渲染器。無論何時調整表面或繪製新幀,系統都會調用此渲染器。我們也可以通過傳入不同的渲染器來支持OpenGL ES1.x,但是由於API不同,我們需要編寫不同的代碼。在本課中,我們只關注支持OpenGL ES2。

最後,我們將內容視圖設置爲GLSurfaceView,它告訴Activity的內容應由我們的OpenGL表面填充。要進入OpenGL,就這麼簡單!

    @Override
    protected void onResume() {
        super.onResume();
        // 當Activity的onResume()方法被調用時,必須調用GLSurfaceView的onResume()方法
        mGLSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 當Activity的onPause()方法被調用時,必須調用GLSurfaceView的onPause()方法
        mGLSurfaceView.onPause();
    }

GLSurfaceView需要我們在Activity的onResume()和onPaused()時調用它的onResume()和onPause()方法。 我們在此處添加調用以完善我們的Activity。

可視化3D世界

在本節中,我們將開始研究OpenGL ES2的工作原理以及如何開始在屏幕上繪製內容。 在LessonOneActivity中,我們將自定義的GLSurfaceView.Renderer傳遞給GLSurfaceView。 渲染器有三個重要的方法,系統會自動調用這些方法:

    @Override
    public void onSurfaceCreated(GL10 glUnused, EGLConfig eglConfig) {

    }

首次創建Surface時會調用此方法。 如果我們丟失Surface上下文並且稍後由系統重新創建它,也將調用此方法。

    @Override
    public void onSurfaceChanged(GL10 glUnused, int width, int height) {

    }

只要Surface發生變化,就會調用它; 例如,從縱向切換到橫向時。 在創建Surface後也會調用它。

    @Override
    public void onDrawFrame(GL10 glUnused) {

    }

只要是繪製新幀的時候就會調用它。

您可能已經注意到傳入的GL10實例稱爲glUnused。 使用OpenGL ES2繪圖時我們不使用它; 相反,我們使用GLES20類的靜態方法。 GL10參數僅在那裏,因爲相同的接口用於OpenGL ES1.x.

在我們的渲染器可以顯示任何內容之前,我們需要顯示一些內容。 在OpenGL ES2中,我們通過指定數字數組傳遞內容。 這些數字可以表示位置,顏色或我們需要的任何其他內容。 在這個演示中,我們將顯示三個三角形。

    // New class members
    /** Store our model data in a float buffer. */
    private final FloatBuffer mTriangle1Vertices;
    private final FloatBuffer mTriangle2Vertices;
    private final FloatBuffer mTriangle3Vertices;
    /** How many bytes per float. */
    private final int mBytesPerFloat = 4;

    /**
     * Initialize the model data.
     */
    public LessonOneRenderer() {
        // This triangle is red, green, and blue.
        final float[] triangle1VerticesData = {
                // X, Y, Z,
                // R, G, B, A
                -0.5f, -0.25f, 0.0f,
                1.0f, 0.0f, 0.0f, 1.0f,

                0.5f, -0.25f, 0.0f,
                0.0f, 0.0f, 1.0f, 1.0f,

                0.0f, 0.559016994f, 0.0f,
                0.0f, 1.0f, 0.0f, 1.0f};

    ...

        // Initialize the buffers.
        mTriangle1Vertices = ByteBuffer.allocateDirect(triangle1VerticesData.length * mBytesPerFloat)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();

    ...

        mTriangle1Vertices.put(triangle1VerticesData).position(0);

    ...
    }

那麼,這些是什麼意思? 如果你曾經使用過OpenGL 1,你可能會習慣這樣做:

    glBegin(GL_TRIANGLES);
    glVertex3f(-0.5f, -0.25f, 0.0f);
    glColor3f(1.0f, 0.0f, 0.0f);
    ...
    glEnd();

這些方法在OpenGL ES2中不起作用。我們不是通過一堆方法調用來定義點,而是定義一個數組。 讓我們再看看我們的數組:

    final float[] triangle1VerticesData = {
            // X, Y, Z,
            // R, G, B, A
            -0.5f, -0.25f, 0.0f,
            1.0f, 0.0f, 0.0f, 1.0f,
            ...

這代表三角形的一個點。 我們設置了前三個數字代表位置(X,Y和Z),後四個數字代表顏色(紅色,綠色,藍色和alpha(透明度))。 您不必太擔心如何定義此數組; 請記住,當我們想要在OpenGL ES2中繪製內容時,我們需要以塊的形式傳遞數據,而不是一次傳遞一個。

理解緩衝區

    // Initialize the buffers.
    mTriangle1Vertices = ByteBuffer.allocateDirect(triangle1VerticesData.length * mBytesPerFloat)
    .order(ByteOrder.nativeOrder()).asFloatBuffer();
    ...
    mTriangle1Vertices.put(triangle1VerticesData).position(0);

我們在Android上使用Java進行編碼,但OpenGL ES2的底層實現實際上是用C語言編寫的。在我們將數據傳遞給OpenGL之前,我們需要將其轉換爲一種它能夠理解的形式。 Java和本機系統可能不會以相同的順序存儲它們的字節,因此我們使用一組特殊的緩衝區類並創建一個足夠大的ByteBuffer來保存我們的數據,並告訴它使用本機字節順序存儲其數據。 然後我們將它轉換爲FloatBuffer,以便我們可以使用它來保存浮點數據。 最後,我們將數組複製到緩衝區中。

這個緩衝區的東西可能看起來很混亂(當我第一次遇到它時候也是這樣認爲!),但請記住,在將數據傳遞給OpenGL之前,我們需要做一個額外的步驟。 我們的緩衝區現在可以用於將數據傳遞到OpenGL。

另外,float buffers are slow on Froyo and moderately faster on Gingerbread,所以你可能不希望經常更換它們。

理解矩陣

    // New class definitions
    /**
     * Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
     * it positions things relative to our eye.
     */
    private float[] mViewMatrix = new float[16];

    @Override
    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
        // Set the background clear color to gray.
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);

        // Position the eye behind the origin.
        final float eyeX = 0.0f;
        final float eyeY = 0.0f;
        final float eyeZ = 1.5f;

        // We are looking toward the distance
        final float lookX = 0.0f;
        final float lookY = 0.0f;
        final float lookZ = -5.0f;

        // Set our up vector. This is where our head would be pointing were we holding the camera.
        final float upX = 0.0f;
        final float upY = 1.0f;
        final float upZ = 0.0f;

        // Set the view matrix. This matrix can be said to represent the camera position.
        // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
        // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
        Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);

    ...
    }

另一個“有趣”的主題是矩陣! 無論何時進行3D編程,這些都將成爲您最好的朋友,因此您需要很好地瞭解它們。

當我們的Surface被創建時,我們要做的第一件事就是將清除屏幕的顏色設置爲灰色。 alpha部分也已設置爲灰色,但我們在本課程中沒有進行Alpha混合,因此該值未使用。 我們只需要設置一次清除屏幕的顏色顏色,因爲我們以後不會更改它。

我們要做的第二件事是設置我們的視圖矩陣。 我們使用了幾種不同類型的矩陣,它們都做了一些重要的事情:

  1. 模型矩陣。 該矩陣用於在“世界”中的某處放置模型。例如,如果您有一輛汽車的模型,並且您希望它位於東邊1000米處,您將使用模型矩陣來執行此操作。
  2. 視圖矩陣。 該矩陣代表相機。如果我們想要查看我們位於東邊1000米處的汽車,我們也必須向東移動1000米(另一種思考方式是我們保持靜止,世界其他地方向西移動1000米)。我們使用視圖矩陣來做到這一點。
  3. 投影矩陣。 由於我們的屏幕是二維平面的,我們需要進行最後的轉換,將我們的視圖“投影”到我們的屏幕上並獲得漂亮的3D視角。 這就是投影矩陣的用途。

你可以找到對矩陣的一個很好的解釋SongHo’s OpenGL Tutorials。 我建議你多閱讀幾次,直到你理解爲止; 別擔心,我也閱讀了好幾次才理解它!

在OpenGL 1中,模型矩陣與視圖矩陣是結合在一起的。Camera被假設放在了(0,0,0)位置並且面向-Z方向。

我們不需要手工構建這些矩陣。 Android有一個Matrix幫助程序類,可以爲我們做繁重的工作。 在這裏,我爲攝像機創建了一個視圖矩陣,它位於原點後面,朝向遠處。

定義頂點和片元着色器

    final String vertexShader =
            "uniform mat4 u_MVPMatrix;      \n"     // A constant representing the combined model/view/projection matrix.

                    + "attribute vec4 a_Position;     \n"     // Per-vertex position information we will pass in.
                    + "attribute vec4 a_Color;        \n"     // Per-vertex color information we will pass in.

                    + "varying vec4 v_Color;          \n"     // This will be passed into the fragment shader.

                    + "void main()                    \n"     // The entry point for our vertex shader.
                    + "{                              \n"
                    + "   v_Color = a_Color;          \n"     // Pass the color through to the fragment shader.
                    // It will be interpolated across the triangle.
                    + "   gl_Position = u_MVPMatrix   \n"     // gl_Position is a special variable used to store the final position.
                    + "               * a_Position;   \n"     // Multiply the vertex by the matrix to get the final point in
                    + "}                              \n";    // normalized screen coordinates.

在OpenGL ES2中,我們想要在屏幕上顯示的任何內容首先必須通過頂點和片元着色器。好消息是這些着色器並不像它們看起來那麼複雜。頂點着色器對每個頂點執行操作,這些操作的結果用於片元着色器,對每個像素執行額外的計算。

每個着色器基本上由輸入,輸出和程序組成。首先,我們定義一個uniform類型變量,它是一個包含所有變換的組合矩陣。用於將所有頂點投影到屏幕上。然後我們爲位置和顏色定義兩個attribute類型變量。這些屬性將從我們之前定義的緩衝區中讀取,並指定每個頂點的位置和顏色。然後我們定義一個varying類型變量,它在三角形上進行插值計算,並將其傳遞給片元着色器。當它到達片元着色器時,它將爲每個像素保存一個插值。

假設我們定義了一個三角形的三個點分別是紅色、綠色和藍色,我們調整它的大小,使其佔據屏幕上的10個像素。 當片元着色器運行時,它將爲每個像素包含不同的varying類型顏色。 在某一點上, varying 類型顏色可能是紅色,也可能是在紅色和藍色之間,還有可能是更紫色的顏色。

除了設置顏色外,我們還告訴OpenGL頂點的最終位置應該在屏幕上的具體位置。 然後我們定義片元着色器:

    final String fragmentShader =
            "precision mediump float;       \n"     // Set the default precision to medium. We don't need as high of a
                    // precision in the fragment shader.
                    + "varying vec4 v_Color;          \n"     // This is the color from the vertex shader interpolated across the
                    // triangle per fragment.
                    + "void main()                    \n"     // The entry point for our fragment shader.
                    + "{                              \n"
                    + "   gl_FragColor = v_Color;     \n"     // Pass the color directly through the pipeline.
                    + "}                              \n";

這是片元着色器,它實際上會將東西顯示到屏幕上。 在這個着色器中,我們從頂點着色器中獲取varying類型的顏色值,然後直接將其傳遞給OpenGL。 該點已經按像素插值,因爲片元着色器針對將要繪製的每個像素運行。

更多信息請參考OpenGL ES 2 quick reference card

將着色器加載到OpenGL中

        // Load in the vertex shader.
        int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

        if (vertexShaderHandle != 0)
        {
            // Pass in the shader source.
            GLES20.glShaderSource(vertexShaderHandle, vertexShader);

            // Compile the shader.
            GLES20.glCompileShader(vertexShaderHandle);

            // Get the compilation status.
            final int[] compileStatus = new int[1];
            GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

            // If the compilation failed, delete the shader.
            if (compileStatus[0] == 0)
            {
                GLES20.glDeleteShader(vertexShaderHandle);
                vertexShaderHandle = 0;
            }
        }

        if (vertexShaderHandle == 0)
        {
            throw new RuntimeException("Error creating vertex shader.");
        }

首先,我們創建着色器對象。如果成功,我們將獲得對象的引用。然後我們使用這個引用來傳遞着色器源代碼,然後我們編譯它。我們可以從OpenGL獲取狀態,看看它是否成功編譯。如果有錯誤,我們可以使用GLES20.glGetShaderInfoLog(着色器)找出原因。 我們按照相同的步驟加載片元着色器。

將頂點和片段着色器鏈接到一個程序中

        // Create a program object and store the handle to it.
        int programHandle = GLES20.glCreateProgram();

        if (programHandle != 0)
        {
            // Bind the vertex shader to the program.
            GLES20.glAttachShader(programHandle, vertexShaderHandle);

            // Bind the fragment shader to the program.
            GLES20.glAttachShader(programHandle, fragmentShaderHandle);

            // Bind attributes
            GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
            GLES20.glBindAttribLocation(programHandle, 1, "a_Color");

            // Link the two shaders together into a program.
            GLES20.glLinkProgram(programHandle);

            // Get the link status.
            final int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);

            // If the link failed, delete the program.
            if (linkStatus[0] == 0)
            {
                GLES20.glDeleteProgram(programHandle);
                programHandle = 0;
            }
        }

        if (programHandle == 0)
        {
            throw new RuntimeException("Error creating program.");
        }

在我們使用頂點和片元着色器之前,我們需要將它們綁定到一個程序中。 這是將頂點着色器的輸出與片元着色器的輸入相連接的內容。 這也是讓我們從程序傳遞輸入並使用着色器繪製形狀的原因。

我們創建一個新的程序對象,如果成功,我們就會附加我們的着色器。 我們希望將位置和顏色作爲屬性傳遞,因此我們需要綁定這些屬性。 然後我們將着色器鏈接在一起。

//New class members
/** This will be used to pass in the transformation matrix. */
private int mMVPMatrixHandle;
 
/** This will be used to pass in model position information. */
private int mPositionHandle;
 
/** This will be used to pass in model color information. */
private int mColorHandle;
 
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
{
    ...
 
    // Set program handles. These will later be used to pass in values to the program.
    mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
    mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
    mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");
 
    // Tell OpenGL to use this program when rendering.
    GLES20.glUseProgram(programHandle);
}

在我們成功鏈接着色器程序之後,我們完成了幾項任務,以便我們可以實際使用它。 第一個任務是獲取引用,以便我們可以將數據傳遞到着色器程序中。 然後我們告訴OpenGL在繪圖時使用這個着色器程序。 由於我們在本課中只使用了一個着色器程序,因此我們可以將它放在onSurfaceCreated()而不是onDrawFrame()中。

設置透視投影

// New class members
/** Store the projection matrix. This is used to project the scene onto a 2D viewport. */
private float[] mProjectionMatrix = new float[16];
 
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height)
{
    // Set the OpenGL viewport to the same size as the surface.
    GLES20.glViewport(0, 0, width, height);
 
    // Create a new perspective projection matrix. The height will stay the same
    // while the width will vary as per aspect ratio.
    final float ratio = (float) width / height;
    final float left = -ratio;
    final float right = ratio;
    final float bottom = -1.0f;
    final float top = 1.0f;
    final float near = 1.0f;
    final float far = 10.0f;
 
    Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}

onSurfaceChanged()被調用至少一次,並且每當我們的surface被改變時。 由於我們只需要在我們投影到的屏幕發生變化時重置投影矩陣,onSurfaceChanged()就是理想的選擇。

把東西畫到屏幕上!

    // New class members
    /**
     * Store the model matrix. This matrix is used to move models from object space (where each model can be thought
     * of being located at the center of the universe) to world space.
     */
    private float[] mModelMatrix = new float[16];

    @Override
    public void onDrawFrame(GL10 glUnused)
    {
        GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

        // Do a complete rotation every 10 seconds.
        long time = SystemClock.uptimeMillis() % 10000L;
        float angleInDegrees = (360.0f / 10000.0f) * ((int) time);

        // Draw the triangle facing straight on.
        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
        drawTriangle(mTriangle1Vertices);

        ...
    }

這是實際顯示在屏幕上的內容。 首先清除了屏幕,因此我們沒有得到任何奇怪的鏡面效果。我們希望三角形在屏幕上能有平滑的動畫,我們使用時間旋轉三角形。 每當您在屏幕上製作動畫時,通常最好使用時間而不是幀速率。

實際繪圖在drawTriangle中完成:

    // New class members
    /** Allocate storage for the final combined matrix. This will be passed into the shader program. */
    private float[] mMVPMatrix = new float[16];

    /** How many elements per vertex. */
    private final int mStrideBytes = 7 * mBytesPerFloat;

    /** Offset of the position data. */
    private final int mPositionOffset = 0;

    /** Size of the position data in elements. */
    private final int mPositionDataSize = 3;

    /** Offset of the color data. */
    private final int mColorOffset = 3;

    /** Size of the color data in elements. */
    private final int mColorDataSize = 4;

    /**
     * Draws a triangle from the given vertex data.
     *
     * @param aTriangleBuffer The buffer containing the vertex data.
     */
    private void drawTriangle(final FloatBuffer aTriangleBuffer)
    {
        // Pass in the position information
        aTriangleBuffer.position(mPositionOffset);
        GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
                mStrideBytes, aTriangleBuffer);

        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Pass in the color information
        aTriangleBuffer.position(mColorOffset);
        GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false,
                mStrideBytes, aTriangleBuffer);

        GLES20.glEnableVertexAttribArray(mColorHandle);

        // This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
        // (which currently contains model * view).
        Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);

        // This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
        // (which now contains model * view * projection).
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);

        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
    }

你還記得我們最初創建渲染器時定義的那些緩衝區嗎? 我們終於可以使用它們了。 我們需要告訴OpenGL如何使用GLES20.glVertexAttribPointer()來使用這些數據。 讓我們來看看第一個調用。

// Pass in the position information
aTriangleBuffer.position(mPositionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
        mStrideBytes, aTriangleBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);

我們將緩衝區位置設置爲位置偏移量,它位於緩衝區的開頭。然後我們告訴OpenGL使用這些數據並將其提供給頂點着色器並將其應用於我們的position屬性。我們還需要告訴OpenGL每個頂點或步幅之間有多少個元素。

注意:步幅需要以字節爲單位進行定義。雖然我們在頂點之間有7個元素(3個用於位置,4個用於顏色),但實際上我們有28個字節,因爲每個浮點數佔用4個字節。忘記此步驟可能不會導致任何錯誤,但您會想知道爲什麼您在屏幕上看不到任何內容。

最後,我們啓用頂點屬性並轉到下一個屬性進行設置。繼續看代碼,我們構建一個組合矩陣,將點投影到屏幕上。 我們也可以在頂點着色器中執行此操作,但由於它只需要完成一次,所以我們可以只緩存結果。 我們使用GLES20.glUniformMatrix4fv()將最終矩陣傳遞給頂點着色器,GLES20.glDrawArrays()將我們的點轉換爲三角形並將其繪製在屏幕上。

概括

呼! 這是一個很重要的課程,如果你完成了這一課,你會非常開心。 我們學習瞭如何創建OpenGL上下文,傳遞形狀數據,加載頂點和像素着色器,設置轉換矩陣,最後將它們組合在一起。 如果一切順利,您應該會看到類似於下側屏幕截圖的內容。

這一課有很多要消化的內容,你可能需要多次閱讀這些步驟才能理解它。 OpenGL ES2需要更多的設置工作才能開始,但是一旦你完成了這個過程幾次,你就會記住前面的流程。

在Android Market上發佈

在開發應用程序時,我們不希望無法運行這些應用程序的人在市場上看到它們,否則當應用程序在其設備上崩潰時,我們可能會收到大量糟糕的評論和評分。 要防止OpenGL ES2應用程序出現在不支持它的設備上,您可以將其添加到清單中:

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

這告訴市場您的應用程序需要OpenGL ES2,它會將您的應用程序隱藏掉如果設備不支持它。

進一步探索

嘗試更改動畫速度,頂點或顏色,看看會發生什麼!
可以從GitHub上的項目站點下載本課程的完整源代碼

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