Android OpenGL ES3.0 (1)

\最近研究了一下OpenGL ES3.0,參照了網上很多的示例,在這裏總結一部分經驗

1)數據

OpenGL ES3.0 中的3D數據模型是由無數個三角形組成的。例如下面這段數據,就是一個繪製立方體的模型。float數組中每個三個元素代表一個頂點(x,y, z),沒三個頂點(九個元素)代表一個三角形。opengles 的數據特點是在-1到1的範圍表示整個空間。下面的數據就是用來繪製一個長寬高都佔半個空間的立方體。

 

  float vertexes[] = {
                -0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, 0.5f, -0.5f,
                0.5f, 0.5f, -0.5f,
                -0.5f, 0.5f, -0.5f,
                -0.5f, -0.5f, -0.5f,

                -0.5f, -0.5f, 0.5f,
                0.5f, -0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, 0.5f,
                -0.5f, -0.5f, 0.5f,

                -0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, -0.5f,
                -0.5f, -0.5f, -0.5f,
                -0.5f, -0.5f, -0.5f,
                -0.5f, -0.5f, 0.5f,
                -0.5f, 0.5f, 0.5f,

                0.5f, 0.5f, 0.5f,
                0.5f, 0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,

                -0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, -0.5f,
                0.5f, -0.5f, 0.5f,
                0.5f, -0.5f, 0.5f,
                -0.5f, -0.5f, 0.5f,
                -0.5f, -0.5f, -0.5f,

                -0.5f, 0.5f, -0.5f,
                0.5f, 0.5f, -0.5f,
                0.5f, 0.5f, 0.5f,
                0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, 0.5f,
                -0.5f, 0.5f, -0.5f,
        };
       float[] colors = {
            
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,
                1f, 0f, 0f, 1f,

        
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,
                0f, 0f, 0f, 1f,

           
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,
                0f, 0f, 1f, 1f,

          
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,
                0.2f, 1f, 0.2f, 1f,

           
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,
                1f, 1f, 1f, 1f,

          
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f,
                0.3f, 0.4f, 0.5f, 1f
        };

這裏先把數據加載到 FloatBuffer 中

 vertexBuffer=loadData(vertexes);
        colorBuffer=loadData(colors);
   private FloatBuffer loadData(float [] arr){
        ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer  floatBuffer = bb.asFloatBuffer();
        floatBuffer.put(arr);
        floatBuffer.position(0);
        return floatBuffer;
    }

那麼opengl es 是怎麼獲取這些數據的呢,就是用頂點着色器,下面是一段頂點着色器的源碼,3.0必須指定版本好,在Android api裏面是直接把頂點着色器的源碼以String的類型讀取,可以放在文件中,也可以放在String對象裏。下面的這段源碼 layout 關鍵字後面的括號中的location 是在載入數據的時候指定的 ,in 就是指數據是輸入數據。 vec3 是一種類型,可以理解爲矢量數據,用來存放頂點數據。out 是指輸出,將頂點着色器的顏色輸出給片源着色器。片源着色器把頂點着色器中傳遞進來的顏色輸出。

    private final String vertexShaderCode ="//指定版本號\n" +
            "#version 300 es\n" +
            "uniform mat4 transform;" +
            "layout (location = 0) in vec3 aPos;\n" +
            "layout (location = 1) in vec4 aColor;\n" +
            "out vec4 color;\n" +
            "void main()\n" +
            "{\n" +
            "    // gl_Position (內置函數) 賦值位置\n" +
            "    gl_Position = transform*vec4(aPos, 1.0);\n" +
            "    color = aColor;\n" +
            "}";

 

 private final String fragmentShaderCode =
            "#version 300 es\n" +
                    "precision mediump float;\n" +
                    "\n" +
                    "out vec4 fragColor;\n" +
                    "in vec4 color;\n" +
                    "void main()\n" +
                    "{\n" +
                    "    fragColor = color;\n" +
                    "}" ;

那麼這些數據如何讀取,就使用下面的代碼,mProgram是一個int類型,相當於頂點着色器的位置指示。顯示載入着色器的源碼,這裏面GL_VERTEX_SHADER 是頂點着色器用於繪製頂點, GL_FRAGMENT_SHADER是 片源着色器用於繪製顏色。

然後是一個着色器的程序,如果不爲0級說明創建成功了,之後的代碼是連接着色器。連接後會有一個狀態數組返回,第一個用元素就是狀態。載入着色器的程序也類似,先調用載入源碼,之後編譯源碼,最後看返回的數據,如果編譯成功就不爲0.這裏的返回數據就用來傳遞給着色器程序。(vertexShader 和framentShader中存的int值) 。最後要配置使程序相應指定的數據,第一個參數就是之前在着色器中指定的location (GLES30.glVertexAttribPointer(0, VERTEX_POSITION_SIZE, GLES30.GL_FLOAT, false, 0, vertexBuffer); GLES30.glEnableVertexAttribArray(0);) 代碼裏從程序中讀取了兩套,一個是用於繪製頂點,一個是用於繪製顏色, 就是對應頂點着色器中 in  關鍵字修飾的 vec 。  這裏準備工作就做好了。下面就是繪製。

 

    private void initProgram(){
        int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER,vertexShaderCode);
        int fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER,fragmentShaderCode);
        mProgram = GLES30.glCreateProgram();
        if(mProgram != 0 ){
            GLES30.glAttachShader(mProgram,vertexShader);
            GLES30.glAttachShader(mProgram,fragmentShader);
            GLES30.glLinkProgram(mProgram);
            int[] linkStatus = new int[1];
            GLES30.glGetProgramiv(mProgram, GLES30.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES30.GL_TRUE) {
                Log.e(TAG, "連接着色器失敗 ");
                Log.e(TAG, GLES30.glGetProgramInfoLog(mProgram));
                GLES30.glDeleteProgram(mProgram);
                mProgram = 0;
            }
        }
        GLES30.glVertexAttribPointer(0, VERTEX_POSITION_SIZE, GLES30.GL_FLOAT, false, 0, vertexBuffer);
        GLES30.glEnableVertexAttribArray(0);

        GLES30.glVertexAttribPointer(1, VERTEX_COLOR_SIZE, GLES30.GL_FLOAT, false, 0, colorBuffer);
        GLES30.glEnableVertexAttribArray(1);
    }
  private int loadShader(int type,String shaderCode){

        int shader = GLES30.glCreateShader(type);
        if(shader != 0 ){
            GLES30.glShaderSource(shader,shaderCode);
            GLES30.glCompileShader(shader);
            int[] compiled = new int[1];
            GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {
                GLES30.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

繪製的代碼,這裏的transform  是用於圖形變換的,在頂點着色器的源碼中可以看到他是一個mat4類型,他是一個矩陣,網上有大量的文章說明了opengl es中利用矩陣運算,進行圖形轉換的示例,在頂點着色器源碼中也可以看到,是做了矩陣的相乘。這裏opengl es的api中也提供了相應生成變換矩陣的方法。 setIdentityM  就相當於聲明瞭一個矩陣,這個矩陣的需要我們提前在內存中創建,它的長度是固定的。然後 調用 gluniformmatrix4fv就相當於把 modelMatrix的數據傳遞給 頂點着色器中與頂點數據相乘的transform 。

 

    int transform = GLES30.glGetUniformLocation(mProgram, "transform");
        GLES30.glUniformMatrix4fv(transform, 1, false, modelMatrix, 0);

        GLES30.glClearColor(0.2f, 0.1f, 0.3f, 1.0f);
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);


        GLES30.glUseProgram(mProgram);
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 36);
        modelMatrix =new float[16];

        Matrix.setIdentityM(modelMatrix, 0);

        Matrix.rotateM(modelMatrix, 0, 30, 0f, 1f, 0f);
//
        Matrix.rotateM(modelMatrix, 0, 30, 0f, 0f, 1f);

 

發佈了26 篇原創文章 · 獲贊 0 · 訪問量 8038
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章