android-opengles3.0開發(fā)【2】繪制圖形

簡介

android 下 opengles 的繪制圖形簡單來說步驟如下:

  1. 定義圖形頂點數(shù)據(jù)
  2. 編寫/編譯 頂點著色器 和 片段著色器。
  3. 創(chuàng)建程序,將著色器綁定到程序上,然后連接程序。如果著色器中沒有定義屬性的位置,則在綁定著色器之后、連接程序之前,將屬性名稱和位置進(jìn)行綁定。
  4. 使用程序,將圖形頂點數(shù)據(jù)放到相應(yīng)的屬性位置上,然后進(jìn)行繪制。

定義圖形頂點數(shù)據(jù)

浮點型數(shù)組,頂點的順序按逆時針排列。

android 平臺上,app 運行在 jvm 中,內(nèi)存由 jvm 管理,而 opengles 運行在 native 環(huán)境,所以為了式 opengles 能夠訪問圖形頂點數(shù)據(jù),需要把頂點拷貝到 native 內(nèi)存中。

另外,為了方便操作 native 中的頂點字節(jié),將其映射到 FloatBuffer 中,然后就可以像使用數(shù)組一樣使用了。

    //頂點,按逆時針順序排列
    private static final float[] vertices = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f};

    public TriangleRender() {
        //將頂點數(shù)據(jù)拷貝映射到 native 內(nèi)存中,以便opengl能夠訪問
        verticesBuffer = ByteBuffer
                .allocateDirect(vertices.length * BYTES_PER_FLOAT)//直接分配 native 內(nèi)存,不會被gc
                .order(ByteOrder.nativeOrder())//和本地平臺保持一致的字節(jié)序(大/小頭)
                .asFloatBuffer();//將底層字節(jié)映射到FloatBuffer實例,方便使用
        verticesBuffer
                .put(vertices)//將頂點拷貝到 native 內(nèi)存中
                .position(0);//每次 put position 都會 + 1,需要在繪制前重置為0
    }

編寫/編譯著色器

著色器的源代碼可以寫到單獨的文件中,也可以用字符串拼接(本文使用的方式,為了方便)

頂點著色器

  • 第一行指定opengles版本,不指定默認(rèn)為 opengles2.0
  • 第二行聲明一個輸入變量 vPosition,表示頂點的坐標(biāo),4分量向量。layout (location = 0) 用來指定該 vPosition 的屬性位置,后面需要使用該位置將頂點數(shù)據(jù)匹配到 vPosition 上。
  • 后面幾行定義了一個 main() 方法,是著色器的入口,方法中將 vPosition 值賦值給 gl_position 內(nèi)建變量中,表示的是頂點的最終位置。

簡單來說,該著色器的內(nèi)容就是:使用 opengles3.0 版本,將圖形頂點數(shù)據(jù)采用4分量向量的數(shù)據(jù)結(jié)構(gòu)綁定到著色器的第 0 個屬性上,屬性的名字叫 vPosition,著色器執(zhí)行時,將 vPosition 的值傳給用來表示頂點最終位置的內(nèi)建變量 gl_Position。

需要注意的是 layout (location = 0) 不是必須要寫,如果不寫的話,需要在后面著色器綁定到程序之后,程序連接之前使用 glBindAttribLocation 方法綁定。

    //頂點著色器
    private static final String vertextShaderSource =
            "#version 300 es\n"
                    + "layout (location = 0) in vec4 vPosition;\n"
                    + "void main()\n"
                    + "{\n"
                    + "    gl_Position = vPosition;\n"
                    + "}\n";

片段著色器

  • 第一行指定opengles版本,不指定默認(rèn)為 opengles2.0
  • 第二行指明浮點變量的精度
  • 第三行聲明一個輸出變量 fragColor ,4分量向量
  • 給輸出變量賦值
    //片段著色器
    private static final String fragmentShaderSource =
            "#version 300 es                                \n"
                    + "precision mediump float;                     \n"
                    + "out vec4 fragColor;                          \n"
                    + "void main()                                  \n"
                    + "{                                            \n"
                    + "  fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );   \n"
                    + "}                                            \n";

著色器內(nèi)容編輯完之后,想要使用還要經(jīng)過編譯。

    /**
     * 加載著色器源,并編譯
     *
     * @param type         頂點著色器(GL_VERTEX_SHADER)/片段著色器(GL_FRAGMENT_SHADER)
     * @param shaderSource 著色器源(上面編輯的內(nèi)容)
     * @return 著色器
     */
    private int loadShader(int type, String shaderSource) {
        //創(chuàng)建著色器對象
        int shader = GLES30.glCreateShader(type);
        if (shader == 0) return 0;//創(chuàng)建失敗
        //加載著色器源
        GLES30.glShaderSource(shader, shaderSource);
        //編譯著色器
        GLES30.glCompileShader(shader);
        //檢查編譯狀態(tài)
        int[] compiled = new int[1];
        GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e(TAG, GLES30.glGetShaderInfoLog(shader));
            GLES30.glDeleteShader(shader);
            return 0;//編譯失敗
        }

        return shader;
    }

初始化程序

具體的流程下面代碼很清楚,需要注意的就是如果著色器中沒有指定屬性的位置,則需要調(diào)用 glBindAttribLocation 進(jìn)行指定。

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //獲取頂點著色器
        int vertextShader = loadShader(GLES30.GL_VERTEX_SHADER, vertextShaderSource);
        //獲取片段著色器
        int fragmentShader =loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderSource);
        //創(chuàng)建程序
        int tmpProgram = GLES30.glCreateProgram();
        if (tmpProgram == 0) return;//創(chuàng)建失敗
        //綁定著色器到程序
        GLES30.glAttachShader(tmpProgram, vertextShader);
        GLES30.glAttachShader(tmpProgram, fragmentShader);
        
        //綁定屬性位置 vPosition :0 著色器中沒有設(shè)定屬性位置時使用
//        GLES30.glBindAttribLocation(tmpProgram, 0, "vPosition");

        //連接程序
        GLES30.glLinkProgram(tmpProgram);
        //檢查連接狀態(tài)
        int[] linked = new int[1];
        GLES30.glGetProgramiv(tmpProgram,GLES30.GL_LINK_STATUS, linked, 0);
        if (linked[0] == 0){
            Log.e(TAG, "tmpProgram linked error");
            Log.e(TAG, GLES30.glGetProgramInfoLog(tmpProgram));
            GLES30.glDeleteProgram(tmpProgram);
            return;//連接失敗
        }
        //保存程序,后面使用
        program = tmpProgram;

        //設(shè)置清除渲染時的顏色
        GLES30.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    }

使用程序繪制

    @Override
    public void onDrawFrame(GL10 gl) {
        //擦除屏幕
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
        //使用程序
        GLES30.glUseProgram(program);
        //獲取 vPosition 屬性的位置
        int vposition = GLES30.glGetAttribLocation(program, "vPosition");
        //加載頂點數(shù)據(jù)到 vPosition 屬性位置
        GLES30.glVertexAttribPointer(vposition,3,GLES30.GL_FLOAT,false,0,verticesBuffer);
        GLES30.glEnableVertexAttribArray(vposition);
        //繪制
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES,0,3);
    }

總結(jié)

本文通過繪制一個三角形,梳理了 opengles 繪制圖形的流程,著色器、程序的使用方法。

項目地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容