aliang study opengles之opengles開篇

Android Opengles開篇

前言:opengles感覺算是一個很"古老"的技術了,從1.0-2.0-3.0經歷 了三個版本了
本篇及以后講述的都是建立在opengles2.0基礎上進行demo演示的。可能有些人
對opengl比較陌生,但在Android手機當中和圖像相關的東西,都會涉及到Opengl。

一.什么是Opengles?

Opengl這東西是有的,為什么要在后面加上一個ES呢?它主要是針對于嵌入式設備,對原本Opengl
進行了裁剪的縮減庫.Opengles的功能比Opengl要少一些。說白了就是Opengles中你要寫的代碼就更多了,
因為在Opengl中有的一些基本圖形被縮減到只有點,線,三角形了。


二.廢話就不. 多說了,開篇就用opengles畫個三角形,對其中的基本概念進行敘述。

開發工具就用是Android studio2.1
在AndroidStudio中有個GLSL的插件,可以識別著色器語言的.
GLSL是opengles中一個關鍵的文件,它主要是分為Vertex(頂點)Shader的glsl以及Fragment(片段)Shader的glsl.對它們的編寫就決定最終物體相關屬性是怎么設置的了。
對于GLES的說明會放到后面進行。這里畫三角形,對GLSL編寫也是很簡單的,就直接在代碼里加注釋說明。


以建造房子為例子來闡述一個三角形的繪制

三.首先是GLES的簡單編寫

1.畫筆準備:

Vertex和Frament在opengles中是以字符串的形式被載入,所以在assets里面創建兩個資源文件
vertex.glsl和frag.glsl

vertex.glsl

uniform mat4 uMVPMatrix;  //頂點最終變換矩陣
attribute vec3 aPostion;  //頂點坐標值(x,y,z)
attribute vec4 aColor;  //頂點的顏色(R,G,B,A)
varying vec4 vColor;   //傳遞給片段著色器的顏色值,varying聲明的變量都是要傳遞給fragment的

void main(){
    //gl_Position是glsl的內置變量,記錄頂點最終的位置 。
    //vec4(aPostion,1)為是矩陣想成匹配
    gl_Position = uMVPMatrix * vec4(aPostion,1);
    vColor = aColor;//將頂點顏色值傳遞給fragment
}

frag.glsl

precision mediump float;  //聲明float的精度,一般情況下都是用mediump的
varying vec4 vColor; //接收從頂點glsl傳過來的顏色參數
//對片段顏色的具體處理
void main(){
    //直接將頂點傳過來的顏色參數賦值給了內置變量gl_fragColor,也就給fragment上色了
    gl_FragColor = vColor;
}

2.將shader(glsl)載入到系統中做畫畫準備:

幫助類的編寫,這個是固定的模板,以后都可以直接進行復用的.
這個幫主類的簡單流程如下:

  1. 將asset中的vertext.glsl和frag.glsl
    1. 方法名為:public static String loadFromAssetsFile(String fname, Resources r);
  2. 獲取vertex的ID和fragment的ID
    1. 方法名為:public static int loadShader(int shaderType, String source )
  3. 通過上面獲得的ID,將vertext和fragment組合成programe
    1. public static int createProgram(String vertexSource, String fragmentSource)
幫助類ShaderUtil的具體代碼
package com.bn.Sample3_1;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;

//加載頂點Shader與片元Shader的工具類
public class ShaderUtil {
    // 加載制定shader的方法
    public static int loadShader(int shaderType, // shader的類型
                                                    // GLES20.GL_VERTEX_SHADER(頂點)
                                                    // GLES20.GL_FRAGMENT_SHADER(片元)
            String source // shader的腳本字符串
    ) {
        // 創建一個新shader
        int shader = GLES20.glCreateShader(shaderType);
        // 若創建成功則加載shader
        if (shader != 0) {
            // 加載shader的源代碼
            GLES20.glShaderSource(shader, source);
            // 編譯shader
            GLES20.glCompileShader(shader);
            // 存放編譯成功shader數量的數組
            int[] compiled = new int[1];
            // 獲取Shader的編譯情況
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {// 若編譯失敗則顯示錯誤日志并刪除此shader
                Log.e("ES20_ERROR", "Could not compile shader " + shaderType
                        + ":");
                Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

    // 創建shader程序的方法
    public static int createProgram(String vertexSource, String fragmentSource) {
        // 加載頂點著色器
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }

        // 加載片元著色器
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }

        // 創建程序
        int program = GLES20.glCreateProgram();
        // 若程序創建成功則向程序中加入頂點著色器與片元著色器
        if (program != 0) {
            // 向程序中加入頂點著色器
            GLES20.glAttachShader(program, vertexShader);
            checkGlError("glAttachShader");
            // 向程序中加入片元著色器
            GLES20.glAttachShader(program, pixelShader);
            checkGlError("glAttachShader");
            // 鏈接程序
            GLES20.glLinkProgram(program);
            // 存放鏈接成功program數量的數組
            int[] linkStatus = new int[1];
            // 獲取program的鏈接情況
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            // 若鏈接失敗則報錯并刪除程序
            if (linkStatus[0] != GLES20.GL_TRUE) {
                Log.e("ES20_ERROR", "Could not link program: ");
                Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }

    // 檢查每一步操作是否有錯誤的方法
    public static void checkGlError(String op) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e("ES20_ERROR", op + ": glError " + error);
            throw new RuntimeException(op + ": glError " + error);
        }
    }

    // 從sh腳本中加載shader內容的方法
    public static String loadFromAssetsFile(String fname, Resources r) {
        String result = null;
        try {
            InputStream in = r.getAssets().open(fname);
            int ch = 0;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((ch = in.read()) != -1) {
                baos.write(ch);
            }
            byte[] buff = baos.toByteArray();
            baos.close();
            in.close();
            result = new String(buff, "UTF-8");
            result = result.replaceAll("\\r\\n", "\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

準備工作做完,就需要開始繪制三角形了。

3.Trangle 三角形類編寫

注意事項:1.opengles采用的是左手坐標系x->大拇指,y->中指,z->食指
2.其中涉及到的一些矩陣,后續會講到。

//三角形
public class Triangle
{
    public static float[] mProjMatrix = new float[16];//4x4矩陣 投影用
    public static float[] mVMatrix = new float[16];//攝像機位置朝向9參數矩陣
    public static float[] mMVPMatrix;//最后起作用的總變換矩陣
    
    int mProgram;//自定義渲染管線程序id
    int muMVPMatrixHandle;//總變換矩陣引用id
    int maPositionHandle; //頂點位置屬性引用id  
    int maColorHandle; //頂點顏色屬性引用id  
    String mVertexShader;//頂點著色器         
    String mFragmentShader;//片元著色器
    static float[] mMMatrix = new float[16];//具體物體的移動旋轉矩陣,旋轉、平移
    
    FloatBuffer   mVertexBuffer;//頂點坐標數據緩沖
    FloatBuffer   mColorBuffer;//頂點著色數據緩沖
    int vCount=0;   
    float xAngle=0;//繞x軸旋轉的角度
    public Triangle(MyTDView mv)
    {       
        //初始化頂點坐標與著色數據
        initVertexData();
        //初始化shader
        initShader(mv);
    }
   
    public void initVertexData()
    {
        //頂點坐標數據的初始化
        vCount=3;  
        final float UNIT_SIZE=0.2f;
        float vertices[]=new float[]
        {
            -4*UNIT_SIZE,0,
            0,0,-4*UNIT_SIZE,
            0,4*UNIT_SIZE,0,0,
            -4*UNIT_SIZE,0,0
        };
        
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        mVertexBuffer = vbb.asFloatBuffer();
        mVertexBuffer.put(vertices);
        mVertexBuffer.position(0);
        
        float colors[]=new float[]
        {
                1,0,0,1,
                0,0,1,0,
                0,1,0,0
        };
        
        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
        cbb.order(ByteOrder.nativeOrder());
        mColorBuffer = cbb.asFloatBuffer();
        mColorBuffer.put(colors);
        mColorBuffer.position(0);
    }

    //初始化shader
    public void initShader(MyTDView mv)
    {
        //加載頂點著色器的腳本內容
        mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
        //加載片元著色器的腳本內容
        mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());  
        //基于頂點著色器與片元著色器創建程序
        mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
        //獲取程序中頂點位置屬性引用id  
        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        //獲取程序中頂點顏色屬性引用id  
        maColorHandle= GLES20.glGetAttribLocation(mProgram, "aColor");
        //獲取程序中總變換矩陣引用id
        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");  
    }
    
    int i=0;
    public void drawSelf()
    {        
         //制定使用某套shader程序
         GLES20.glUseProgram(mProgram);        
         //初始化變換矩陣
         Matrix.setRotateM(mMMatrix,0,0,0,1,0);
         i=1;
         if(i == 1){
             for(int j=0;j<mMMatrix.length;j++){
                 Log.i("liang.chen","item"+j+" is:"+mMMatrix[j]);
             }
             
         }
         i++;
         
         //設置沿Z軸正向位移1
         Matrix.translateM(mMMatrix,0,0,0,-1);
         //設置繞x軸旋轉
         Matrix.rotateM(mMMatrix,0,xAngle,0,0,1);
         //
         GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0); 
         //為畫筆指定頂點位置數據
         GLES20.glVertexAttribPointer(
                maPositionHandle,   
                3, 
                GLES20.GL_FLOAT, 
                false,
                3*4,   
                mVertexBuffer
         );
         GLES20.glVertexAttribPointer  
         (
                maColorHandle,
                4,
                GLES20.GL_FLOAT,
                false,
                4*4,
                mColorBuffer
         );
         //允許頂點位置數據數組
         GLES20.glEnableVertexAttribArray(maPositionHandle);  
         GLES20.glEnableVertexAttribArray(maColorHandle);  
         //繪制三角形
         GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); 
    }
    public static float[] getFianlMatrix(float[] spec)
    {
        mMVPMatrix=new float[16];
        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);        
        return mMVPMatrix;
    }
}

現在對代碼的==關鍵點==進行說明:

  • 四個重要的矩陣mMMatrix,mVMatrix,mProjMatrix,mMVPMatrix

    1. mMMatrix指的是物體本身位置,角度的狀態矩陣
    2. mVMatrix指的是人眼看物體時候的位置,角度的狀態矩陣
    3. mProjMatrix指的是物體成像所處的透視環境狀態
    4. mMVPMatrix指的是mMMatrix,mVMatrix,mProjMatrix三者結合的矩陣
  • 其它參數說明:

    1. mVertexBuffer指的是vertex buffer ,將頂點數據燦在vertex buffer里面后后續的管線操作就可以直接從buffer取的數據,這樣加快了數據的讀取
    2. mColorBuffer 類似
  • 局部代碼說明:

    float vertices[]=new float[]
      {
          -4*UNIT_SIZE,0,
          0,0,-4*UNIT_SIZE,
          0,4*UNIT_SIZE,0,0,
      };
    

    頂點的坐標的,前面說過了opengl采用的是左手坐標系

       float colors[]=new float[]
        {
                1,0,0,1,
                0,0,1,0,
                0,1,0,0
        };
    

    三個頂點的顏色值采用的是RGBA

    1. Matrix.setRotateM(mMMatrix,0,0,1,0,0),是將mMMatrix初始化為單位矩陣

     GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0); 
         //為畫筆指定頂點位置數據
         GLES20.glVertexAttribPointer(
                maPositionHandle,   
                3, 
                GLES20.GL_FLOAT, 
                false,
                3*4,   
                mVertexBuffer
         );
         GLES20.glVertexAttribPointer  
         (
                maColorHandle,
                4,
                GLES20.GL_FLOAT,
                false,
                4*4,
                mColorBuffer
         );
    

    這兩個的作用是對頂點屬性值進行賦值的,以第一個glVertexAttribPointer為例
    ==maPositionHandle ==是vertex shader中對應的位置屬性值,==3==指的是坐標點是以頂點數組中三個值為一個頂點,==GLES20.GL_FLOAT==是頂點數組中數據類型,==34==值的是Buffer中一個頂點的大小因為三個float組成一個頂點所以是34,mVertexBuffer就是頂點坐標數組轉成的buffer了

    5.GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
    Android的openlgles的圖元有點,線,三角形,所有的圖形就靠它們來組成,因為這里
    是直接畫三角形,所以就用GLES20.GL_TRAINGLES

    這篇就直觀上講解了opengles繪制一個簡單圖形是怎么繪制的。下篇開始講知識點。
    代碼地址:http://pan.baidu.com/s/1dFcjdZj

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容