Android多媒體之GL-ES戰記第二集--謎團立方

張風捷特烈發表於2019-01-12

上集回顧

旁白:上集說到,為了獲取黑龍寶藏,勇者集結,共闖黑龍洞穴
經過一路艱辛,終於過了第四副本,前面還有什麼困難等待著他們?一起收看


第五副本:龍之圖陣

1.第一關卡:畫一個矩形

NPC:隱藏任務,解謎:GLES20.GL_TRIANGLE_STRIP繪製方式
發現逆時針畫的點貌似連的方式有點問題,貌似是先繪製2->3->4,在繪製1->2->3
因為2->3->4的三角形被1->2->3的等三角形遮住了,後來居上是程式設計的共識

Android多媒體之GL-ES戰記第二集--謎團立方

static float sCoo[] = {   //以逆時針順序
        -0.5f, 0.5f, 0.0f, // 左上
        -0.5f, -0.5f, 0.0f, // 左下
        0.5f, -0.5f, 0.0f, // 右下
        0.5f, 0.5f, 0.0f, //右上
};

float colors[] = new float[]{
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
};

---->[Rectangle#draw]---------
//繪製三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
複製程式碼

怎麼能讓1->2->32->3->4不會重疊?--點調下順序就行了
1->2->4->3

正方形

static float sCoo[] = {   //以逆時針順序
        -0.5f, 0.5f, 0.0f, // 1.
        -0.5f, -0.5f, 0.0f, // 2.
        0.5f, 0.5f, 0.0f, //4.
        0.5f, -0.5f, 0.0f, // 3.
};
複製程式碼

2.第二關卡:畫五邊形

先按照p1->p2->p3->p4->p5的順序,看有什麼效果
意料之內,先把p1->p2->p3->p4四點畫完,再畫第五點

五點.jpg

分析圖

怎麼調點呢?恩...好吧,哥要開始吹牛了
我這麼隨便一想,在滿足正方形的條件下,動一下p5點,就行了,然後讓p5上漂

p5上漂.png

static float sCoo[] = {   //以逆時針順序
        -0.5f, 0.5f, 0.0f, // p1
        0.0f, 0.8f, 0.0f, //p5
        -0.5f, -0.5f, 0.0f, // p2
        0.5f, 0.5f, 0.0f, //p4
        0.5f, -0.5f, 0.0f, // p3
};
複製程式碼

再多邊形也可以用三角形拼出來,不過感覺有點麻煩
解密:GL_TRIANGLE_STRIP:相鄰三個頂點繪製一個三角形


3.第三關卡:索引五邊形--glDrawElements

我就想來著,應該有控制三角形頂點的東西,不然調起來很麻煩

3.1.緩衝的簡單封裝

緩衝頂點資料的套路基本一致,封個方法在GLUtils裡吧,
暫時float(4位元組)和short(2位元組),其他的遇到再說

/**
 * float陣列緩衝資料
 * @param vertexs 頂點
 * @return 獲取浮點形緩衝資料
 */
public static FloatBuffer getFloatBuffer(float[] vertexs) {
    FloatBuffer buffer;
    ///每個浮點數:座標個數* 4位元組
    ByteBuffer qbb = ByteBuffer.allocateDirect(vertexs.length * 4);
    //使用本機硬體裝置的位元組順序
    qbb.order(ByteOrder.nativeOrder());
    // 從位元組緩衝區建立浮點緩衝區
    buffer = qbb.asFloatBuffer();
    // 將座標新增到FloatBuffer
    buffer.put(vertexs);
    //設定緩衝區以讀取第一個座標
    buffer.position(0);
    return buffer;
}

/**
 * short陣列緩衝資料
 * @param vertexs short 陣列
 * @return 獲取short緩衝資料
 */
public static ShortBuffer getShortBuffer(short[] vertexs) {
    ShortBuffer buffer;
    ByteBuffer qbb = ByteBuffer.allocateDirect(vertexs.length * 2);
    qbb.order(ByteOrder.nativeOrder());
    buffer = qbb.asShortBuffer();
    buffer.put(vertexs);
    buffer.position(0);
    return buffer;
}
複製程式碼

3.2:自己控制三角形的形成

我們們從0開始數數,怎麼控制點,看圖你應該能知道

static float sCoo[] = {   //以逆時針順序
        -0.5f, 0.5f, 0.0f, // p0
        -0.5f, -0.5f, 0.0f, // p1
        0.5f, -0.5f, 0.0f, // p2
        0.5f, 0.5f, 0.0f, //p3
        0.0f, 0.8f, 0.0f, //p4
};

//索引陣列
private short[] idx = {
        0, 4, 3,
        1, 3, 0,
        1, 2, 3
};

//索引緩衝
idxBuffer = GLUtils.getShortBuffer(idx);

//繪製
GLES20.glDrawElements(GLES20.GL_TRIANGLES, idx.length,
GLES20.GL_UNSIGNED_SHORT, idxBuffer);
複製程式碼

五邊形.png


4.第四關卡:六邊形

先轉六邊形應該沒有什麼大問題了

六邊形.png

static float sCoo[] = {   //以逆時針順序
        -0.5f, 0.5f, 0.0f, // p0
        -1.0f, 0.0f, 0.0f, // p1
        -0.5f, -0.5f, 0.0f, // p2
        0.5f, -0.5f, 0.0f, //p3
        1.0f, 0.0f, 0.0f, //p4
        0.5f, 0.5f, 0.0f, //p5
};

//索引陣列
private short[] idx = {
        0, 1, 5,
        1, 5, 2,
        2, 5, 4,
        2, 3, 4
};
複製程式碼

第六副本:龍之空間

到這來,我們們幾乎都是在平面,先轉要變立體了
守關boss要發大招了,勇士們,hold住

1.第一關卡:沿著y軸旋轉起來

關於變換第一集講了一些,忘了的可以回去看看
下面是轉一圈的效果,感覺少了一半

旋轉.gif

//圍繞y軸旋轉
Matrix.setRotateM(mOpMatrix, 0, currDeg, 0, 1, 0);
複製程式碼

我讓她一邊移動一邊旋轉,好像可以看出一點東西
不過是轉起來了,等到轉講視野的時候再詳細論述吧

Matrix.setRotateM(mOpMatrix, 0, currDeg, 0, 1, 0);
Matrix.translateM(mOpMatrix, 0, currDeg/360.f, 0,0);
複製程式碼

旋轉+平移


1.第二關卡:空間面

貌似一直z軸我們都是0,動一下唄,紙上和ps上還是各有優點的
紙上畫起來有感覺(但誤差很大),ps裡複用強,先看p0,p1,p2,p3
先從p0點(-0.5,0.5,0.5)入手,然後根據對稱關係,或目測確定其他點,

兩個面


//點位陣列
static float sCoo[] = {
        -0.5f, 0.5f, 0.5f,//p0
        -0.5f, -0.5f, 0.5f,//p1
        -0.5f, -0.5f, -0.5f,//p2
        -0.5f, 0.5f, -0.5f,//p3
        }
        
//索引陣列
private short[] idx = {
        0, 1, 3,
        1, 2, 3,
        }
        
float colors[] = new float[]{
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
        }
複製程式碼

p0-p1-p2-p3.png


2.第二關卡:第二面

一面完成了,第二面p4、p5、p6、p7就好辦了,可以看出來p0,p1和p4,p5是一樣的
索引稍微畫畫也能看出規律,顏色再來那四個,就不貼了
可以看出和我們畫的有點出入,貌似是視口的問題,但不影響圖形本身

p4-p5-p6-p7.png

static float sCoo[] = {
        -0.5f, 0.5f, 0.5f,//p0
        -0.5f, -0.5f, 0.5f,//p1
        -0.5f, -0.5f, -0.5f,//p2
        -0.5f, 0.5f, -0.5f,//p3
        
        -0.5f, 0.5f, 0.5f,//p4
        -0.5f, -0.5f, 0.5f,//p5
        0.5f, -0.5f, 0.5f,//p6
        0.5f, 0.5f, 0.5f,//p7

//索引陣列
private short[] idx = {
        0, 1, 3,
        1, 2, 3,
        
        0+4, 1+4, 3+4,
        1+4, 2+4, 3+4,
複製程式碼

3.第三關卡:其他面

為了方便表述,我給每個面取了名字,分別是:A、B、C、D、E、F

Android多媒體之GL-ES戰記第二集--謎團立方

static float sCoo[] = {
        //A面
        -0.5f, 0.5f, 0.5f,//p0
        -0.5f, -0.5f, 0.5f,//p1
        -0.5f, -0.5f, -0.5f,//p2
        -0.5f, 0.5f, -0.5f,//p3
        //B面
        -0.5f, 0.5f, 0.5f,//p4
        -0.5f, -0.5f, 0.5f,//p5
        0.5f, -0.5f, 0.5f,//p6
        0.5f, 0.5f, 0.5f,//p7
        //C面
        0.5f, 0.5f, 0.5f,//p8
        0.5f, -0.5f, 0.5f,//p9
        0.5f, -0.5f, -0.5f,//p10
        0.5f, 0.5f, -0.5f,//p11
        //D面
        -0.5f, 0.5f, 0.5f,//p12
        0.5f, 0.5f, 0.5f,//p13
        0.5f, 0.5f, -0.5f,//p14
        -0.5f, 0.5f, -0.5f,//p15
        //E面
        -0.5f, -0.5f, 0.5f,//p16
        0.5f, -0.5f, 0.5f,//p17
        0.5f, -0.5f, -0.5f,//p18
        -0.5f, -0.5f, -0.5f,//p19
        //F面
        -0.5f, 0.5f, -0.5f,//p20
        -0.5f, -0.5f, -0.5f,//p21
        0.5f, -0.5f, -0.5f,//p22
        0.5f, 0.5f, -0.5f,//p23
};

//索引陣列
private short[] idx = {
        0, 1, 3,//A
        1, 2, 3,
        0 + 4, 1 + 4, 3 + 4,//B
        1 + 4, 2 + 4, 3 + 4,
        0 + 4 * 2, 1 + 4 * 2, 3 + 4 * 2,//C
        1 + 4 * 2, 2 + 4 * 2, 3 + 4 * 2,
        0 + 4 * 3, 1 + 4 * 3, 3 + 4 * 3,//D
        1 + 4 * 3, 2 + 4 * 3, 3 + 4 * 3,
        0 + 4 * 4, 1 + 4 * 4, 3 + 4 * 4,//E
        1 + 4 * 4, 2 + 4 * 4, 3 + 4 * 4,
        0 + 4 * 5, 1 + 4 * 5, 3 + 4 * 5,//F
        1 + 4 * 5, 2 + 4 * 5, 3 + 4 * 5,
};

float colors[] = new float[]{
        //A
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
        //B
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
        //C
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
        //D
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
        //E
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
        //F
        1f, 1f, 0.0f, 1.0f,//黃
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//藍
        0.19607843f, 1.0f, 0.02745098f, 1.0f,//綠
        1.0f, 0.0f, 1.0f, 1.0f,//紫色
};
複製程式碼

3,4,5.png


第七副本:龍之逆鱗

第六副本中的立方體並不怎麼讓人滿意,視角不怎麼好,
這一副本就來再看看相機和投影以及矩陣

旋轉立方.gif


1.第一關卡:調整相機

一開始眼睛在(0, 0, -3),看下圖你就知道為什麼畫的時候面有問題了
因為相機在後面,而且很正,所以看著就很正...

Android多媒體之GL-ES戰記第二集--謎團立方

// 設定相機位置(檢視矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
        0, 0, -3,
        0f, 0f, 0f,
        0f, 1.0f, 0.0f);
複製程式碼

現在調到後上方(2,2,-5)

 // 設定相機位置(檢視矩陣)
 Matrix.setLookAtM(mViewMatrix, 0,
         2f, 2f, -5.0f,
         0f, 0f, 0f,
         0f, 1.0f, 0.0f);
複製程式碼

調整相機.png


2.第二關卡:旋轉試圖,按紙上的視口繪製

調整視口.png

//初始化變換矩陣
Matrix.setRotateM(mOpMatrix, 0, 130, 0, 1, 0);
複製程式碼

然後再來看繪製的流程,就和紙上的一致了,所以視口很重要

立方.png


第八副本:龍之盛裝 LEVEL1

顏色多沒勁,我們們來貼圖,經歷了這麼多,回頭看看,感慨良多 這個副本將簡單認識貼圖,以後還會有高階的龍之盛裝


1.第一關卡:三角形貼圖

百度了一個小時,愣是沒有把貼圖整明白,敘述的邏輯性欠佳,都是貼個程式碼完事,
貼個完整的還好,但都是一段一段的程式碼...
現在我來捋一遍,最簡單的三角形貼圖的流程,先把視野移到(0,0,-3) 不然肯定變形

貼圖資源.png


1.1:片元程式碼rect_texture.frag
precision mediump float;
varying vec2 aCoordinate;//貼圖座標系
uniform sampler2D vTexture;//貼圖

void main() {
  gl_FragColor=texture2D(vTexture,aCoordinate);
}
複製程式碼

1.2:頂點程式碼:rect_texture.vert
attribute vec3 vPosition;//頂點座標
uniform mat4 uMVPMatrix; //總變換矩陣

attribute vec2 vCoordinate;//貼圖頂點座標
varying vec2 aCoordinate;//貼圖頂點座標--片元變數

void main() {
  gl_Position = uMVPMatrix*vec4(vPosition,1);
  aCoordinate=vCoordinate;
}
複製程式碼

1.3:貼圖三角形:TextureRectangle

就是把顏色控制程式碼換成了貼圖控制程式碼 再繪製的時候接受一個貼圖的id,GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);兩行程式碼貼圖
其餘的基本一致,下面最重要的是這個貼圖id如何獲取

/**
 * 作者:張風捷特烈<br/>
 * 時間:2019/1/9 0009:20:09<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:貼圖測試
 */
public class TextureRectangle {
    private static final String TAG = "Triangle";
    private Context mContext;
    private int mProgram;//OpenGL ES 程式
    private int mPositionHandle;//位置控制程式碼
    private int mColorHandle;//顏色控制程式碼
    private int muMVPMatrixHandle;//頂點變換矩陣控制程式碼
    private FloatBuffer vertexBuffer;//頂點緩衝
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 3*4=12
    static final int COORDS_PER_VERTEX = 3;//陣列中每個頂點的座標數
    static final int c = 1;//陣列中每個頂點的座標數
    static float sCoo[] = {   //以逆時針順序
            -c, c, 0.0f, // p0
            -c, -c, 0.0f, // p1
            c, -c, 0.0f, // p2
    };

    private final float[] textureCoo = {
            0.0f,0.0f,
            0.0f,1.0f,
            1.0f,0.0f,
    };

    static final int TEXTURE_PER_VERTEX = 2;//陣列中每個頂點的座標數
    private final int vertexTextureStride = TEXTURE_PER_VERTEX * 4; // 4*4=16

    private ShortBuffer idxBuffer;
    //索引陣列
    private short[] idx = {
            1, 2, 3,
    };
    private FloatBuffer mTextureCooBuffer;

    public TextureRectangle(Context context) {
        mContext = context;
        //初始化頂點位元組緩衝區
        bufferData();//緩衝頂點資料
        initProgram();//初始化OpenGL ES 程式
    }

    /**
     * 緩衝資料
     */
    private void bufferData() {
        vertexBuffer = GLUtil.getFloatBuffer(sCoo);
        mTextureCooBuffer = GLUtil.getFloatBuffer(textureCoo);
        idxBuffer = GLUtil.getShortBuffer(idx);
    }

    /**
     * 初始化OpenGL ES 程式
     */
    private void initProgram() {
        ////頂點著色
        int vertexShader = GLUtil.loadShaderAssets(mContext,
                GLES20.GL_VERTEX_SHADER, "rect_texture.vert");
        //片元著色
        int fragmentShader = GLUtil.loadShaderAssets(mContext,
                GLES20.GL_FRAGMENT_SHADER, "rect_texture.frag");

        mProgram = GLES20.glCreateProgram();//建立空的OpenGL ES 程式
        GLES20.glAttachShader(mProgram, vertexShader);//加入頂點著色器
        GLES20.glAttachShader(mProgram, fragmentShader);//加入片元著色器
        GLES20.glLinkProgram(mProgram);//建立可執行的OpenGL ES專案

        //獲取頂點著色器的vPosition成員的控制程式碼
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //獲取片元著色器的vColor成員的控制程式碼
        mColorHandle = GLES20.glGetAttribLocation(mProgram, "vCoordinate");
        //獲取程式中總變換矩陣uMVPMatrix成員的控制程式碼
        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    }

    public void draw(float[] mvpMatrix, int texId ) {
        // 將程式新增到OpenGL ES環境中
        GLES20.glUseProgram(mProgram);
        //啟用三角形頂點的控制程式碼
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //啟用三角形頂點顏色的控制程式碼
        GLES20.glEnableVertexAttribArray(mColorHandle);

        //頂點矩陣變換
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0);

        //準備三角頂點座標資料
        GLES20.glVertexAttribPointer(
                mPositionHandle,//int indx, 索引
                COORDS_PER_VERTEX,//int size,大小
                GLES20.GL_FLOAT,//int type,型別
                false,//boolean normalized,//是否標準化
                vertexStride,// int stride,//跨度
                vertexBuffer);// java.nio.Buffer ptr//緩衝

        //準備三角頂點顏色資料
        GLES20.glVertexAttribPointer(
                mColorHandle,
                TEXTURE_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexTextureStride,
                mTextureCooBuffer);
        //繫結紋理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);

        //繪製
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, idx.length, GLES20.GL_UNSIGNED_SHORT, idxBuffer);
        //禁用頂點陣列:
        //禁用index指定的通用頂點屬性陣列。
        // 預設情況下,禁用所有客戶端功能,包括所有通用頂點屬性陣列。
        // 如果啟用,將訪問通用頂點屬性陣列中的值,
        // 並在呼叫頂點陣列命令(如glDrawArrays或glDrawElements)時用於呈現
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}
複製程式碼

1.4:載入紋理

我在GLUtil中封裝了兩個方法

/**
 * 資源id 載入紋理
 * @param ctx 上下文
 * @param resId 資源id
 * @return 紋理id
 */
public static int loadTexture(Context ctx, int resId) {
    Bitmap bitmap = BitmapFactory.decodeResource(ctx.getResources(), resId);
    return loadTexture(ctx, bitmap);
}
/**
 * bitmap 載入紋理
 * @param ctx 上下文
 * @param bitmap bitmap
 * @return 紋理id
 */
public static int loadTexture(Context ctx, Bitmap bitmap) {
    //生成紋理ID
    int[] textures = new int[1];
    //(產生的紋理id的數量,紋理id的陣列,偏移量)
    GLES20.glGenTextures(1, textures, 0);
    int textureId = textures[0];
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    //實際載入紋理(紋理型別,紋理的層次,紋理影象,紋理邊框尺寸)
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    bitmap.recycle();          //紋理載入成功後釋放圖片
    return textureId;
}
複製程式碼

1.5:使用紋理GLRenderer.java
private int textureId;
---->[GLRenderer#onSurfaceCreated]------
textureId = GLUtil.loadTexture(mContext, R.mipmap.mian_a);//初始化紋理

---->[GLRenderer#onDrawFrame]------
mTextureRectangle.draw(mMVPMatrix,textureId);//繪製時使用紋理
複製程式碼

紋理三角形.png


2.第二關卡:矩形紋理

這是一道送分題,沒別的,座標改改就行了

矩形貼圖.png

 static float sCoo[] = {   //以逆時針順序
         -c, c, 0.0f, // p0
         -c, -c, 0.0f, // p1
         c, -c, 0.0f, // p2
         c, c, 0.0f, //p3
 };
 
 private final float[] textureCoo = {
        0.0f,0.0f,
        0.0f,1.0f,
        1.0f,0.0f,
        1.0f,1.0f,
};

//索引陣列
private short[] idx = {
        1, 2, 3,
        0, 1, 3,
};
複製程式碼

有興趣的可以動動貼圖座標系的點看一下,自己瞭解一下貼圖座標系


(出現一道門)NPC:恭喜勇者攻克第八副本,請使用您的立方開啟這道黑龍之門
自此,您將正式踏入黑龍領地,祝您征途順利
我將立方嵌入門中,大門開啟,眼前竟是...

OK,本集結束,下一集:"聖火之光",敬請期待

後記:捷文規範

1.本文成長記錄及勘誤表
專案原始碼 日期 備註
V0.1-github 2018-1-11 Android多媒體之GL-ES戰記第二集--謎團立方
2.更多關於我
筆名 QQ 微信 愛好
張風捷特烈 1981462002 zdl1994328 語言
我的github 我的簡書 我的掘金 個人網站

icon_wx_200.png

相關文章