Android多媒體之GL-ES戰記第一集--勇者集結

前言

1.本系列借花獻佛,結合了不少前人的文章以及書籍,我儘量去總結並用個人思想進行加工
2.OpenGL一直是個人心結,也是時候去解開了,本系列稱不上原創,但每行代碼都有着我思考的痕跡
3.本系列全部的圖片都是[張風捷特烈]所畫,若是有什麼錯誤還請指出,定會最快改正
4.本系列文章容許轉載、截取、公衆號發佈,請保留前言部分,但願廣大讀者悉心指教html


NPC:開場詞

傳說,在這片代碼大陸上,存在一個古老的種族,它們擁有無盡的力量,卻罕有人可以駕馭
多媒體王國中存在一個隱蔽的角落,是這個種族的棲息之地,不多有人敢冒犯那裏
Android多媒體領域有一處:被後人稱爲黑龍洞穴--OpenGL ES,其中埋藏着圖形界的無限財富
勇士們,舉起手中的劍,進發!java


副本一: 黑龍洞口

NPC:黑龍洞口一片漆黑,其中隱藏着什麼規律,勇士們,一塊兒尋找吧!git

1.第一關卡:繪製全屏的紅色

LEVEL 1


1.1:GLSurfaceView的使用
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/9 0009:18:25<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:GL測試視圖
 */
public class GLView extends GLSurfaceView {
    private GLRenderer mRenderer;

    public GLView(Context context) {
        this(context,null);
    }

    public GLView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setEGLContextClientVersion(2);//設置OpenGL ES 2.0 context
        mRenderer = new GLRenderer();
        setRenderer(mRenderer);//設置渲染器
    }
}
複製代碼

1.2:LSurfaceView.Renderer的使用
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/9 0009:18:56<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:GL渲染類
 */
public class GLRenderer implements GLSurfaceView.Renderer {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);//GL視口
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //清除顏色緩存和深度緩存
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    }
}
複製代碼

1.3:Activity中

紅界面.png

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new GLView(this));
    }
}
複製代碼

2.第二關卡:三角形的繪製

三角形.png

2.1:三角形
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/9 0009:20:09<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:三角形
 */
public class Triangle {
    private FloatBuffer vertexBuffer;//頂點緩衝
    private final String vertexShaderCode =//頂點着色代碼
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    " gl_Position = vPosition;" +
                    "}";
    private final String fragmentShaderCode =//片元着色代碼
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    " gl_FragColor = vColor;" +
                    "}";
    private final int mProgram;
    private int mPositionHandle;//位置句柄
    private int mColorHandle;//顏色句柄
    private final int vertexCount = sCoo.length / COORDS_PER_VERTEX;//頂點個數
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 3*4=12
    // 數組中每一個頂點的座標數
    static final int COORDS_PER_VERTEX = 3;
    static float sCoo[] = {   //以逆時針順序
            0.0f, 0.0f, 0.0f, // 頂部
            -1.0f, -1.0f, 0.0f, // 左下
            1.0f, -1.0f, 0.0f  // 右下
    };
    // 顏色,rgba
    float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
    public Triangle() {
        //初始化頂點字節緩衝區
        ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每一個浮點數:座標個數* 4字節
        bb.order(ByteOrder.nativeOrder());//使用本機硬件設備的字節順序
        vertexBuffer = bb.asFloatBuffer();// 從字節緩衝區建立浮點緩衝區
        vertexBuffer.put(sCoo);// 將座標添加到FloatBuffer
        vertexBuffer.position(0);//設置緩衝區以讀取第一個座標
        int vertexShader = GLRenderer.loadShader(
                GLES20.GL_VERTEX_SHADER,//頂點着色
                vertexShaderCode);
        int fragmentShader = GLRenderer.loadShader
                (GLES20.GL_FRAGMENT_SHADER,//片元着色
                        fragmentShaderCode);
        mProgram = GLES20.glCreateProgram();//建立空的OpenGL ES 程序
        GLES20.glAttachShader(mProgram, vertexShader);//加入頂點着色器
        GLES20.glAttachShader(mProgram, fragmentShader);//加入片元着色器
        GLES20.glLinkProgram(mProgram);//建立可執行的OpenGL ES項目
    }
    public void draw() {
        // 將程序添加到OpenGL ES環境中
        GLES20.glUseProgram(mProgram);
        //獲取頂點着色器的vPosition成員的句柄
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //啓用三角形頂點的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //準備三角座標數據
        GLES20.glVertexAttribPointer(
                mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        // 獲取片元着色器的vColor成員的句柄
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        //爲三角形設置顏色
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
        //繪製三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
        //禁用頂點數組
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}
複製代碼

2.2:GL渲染類
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/9 0009:18:56<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:GL渲染類
 */
public class GLRenderer implements GLSurfaceView.Renderer {
    Triangle mTriangle;
    /**
     * 加載做色器
     * @param type  頂點着色 {@link GLES20.GL_VERTEX_SHADER}
     *              片元着色 {@link GLES20.GL_FRAGMENT_SHADER}
     * @param shaderCode 着色代碼
     * @return 做色器
     */
    public static int loadShader(int type, String shaderCode){
        int shader = GLES20.glCreateShader(type);//建立着色器
        GLES20.glShaderSource(shader, shaderCode);//添加着色器源代碼
        GLES20.glCompileShader(shader);//編譯
        return shader;
    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba
        mTriangle = new Triangle();
    }
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);//GL視口
    }
    @Override
    public void onDrawFrame(GL10 gl) {
        //清除顏色緩存和深度緩存
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        mTriangle.draw();
    }
}
複製代碼

2.3:着色器

NPC:勇者,你陣亡了沒...若是如今退出還來得及,這將是一篇宏偉的戰鬥史詩
若是你還想繼續,舉起你手中的劍,同我一塊兒,進發!!!github

着色器.png

/**
 * 加載做色器
 * @param type  頂點着色 {@link GLES20.GL_VERTEX_SHADER}
 *              片元着色 {@link GLES20.GL_FRAGMENT_SHADER}
 * @param shaderCode 着色代碼
 * @return 做色器
 */
public static int loadShader(int type, String shaderCode){
    int shader = GLES20.glCreateShader(type);//建立着色器
    GLES20.glShaderSource(shader, shaderCode);//添加着色器源代碼
    GLES20.glCompileShader(shader);//編譯
    return shader;
}
複製代碼

2.4:渲染器程序

program.png

private final String vertexShaderCode =//頂點着色代碼
        "attribute vec4 vPosition;" +
                "void main() {" +
                " gl_Position = vPosition;" +
                "}";
private final String fragmentShaderCode =//片元着色代碼
        "precision mediump float;" +
                "uniform vec4 vColor;" +
                "void main() {" +
                " gl_FragColor = vColor;" +
                "}";

int vertexShader = GLRenderer.loadShader(
        GLES20.GL_VERTEX_SHADER,//頂點着色
        vertexShaderCode);
int fragmentShader = GLRenderer.loadShader(
        GLES20.GL_FRAGMENT_SHADER,//片元着色
        fragmentShaderCode);
mProgram = GLES20.glCreateProgram();//建立空的OpenGL ES 程序
GLES20.glAttachShader(mProgram, vertexShader);//加入頂點着色器
GLES20.glAttachShader(mProgram, fragmentShader);//加入片元着色器
GLES20.glLinkProgram(mProgram);//建立可執行的OpenGL ES項目
複製代碼

2.5:頂點緩衝

頂點緩衝.png

private FloatBuffer vertexBuffer;//頂點緩衝
private final int vertexCount = sCoo.length / COORDS_PER_VERTEX;//頂點個數
private final int vertexStride = COORDS_PER_VERTEX * 4; // 3*4=12
static final int COORDS_PER_VERTEX = 3;//數組中每一個頂點的座標數
static float sCoo[] = {   //以逆時針順序
        0.0f, 0.0f, 0.0f, // 頂部
        -1.0f, -1.0f, 0.0f, // 左下
        1.0f, -1.0f, 0.0f  // 右下
};

//初始化頂點字節緩衝區
ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每一個浮點數:座標個數* 4字節
bb.order(ByteOrder.nativeOrder());//使用本機硬件設備的字節順序
vertexBuffer = bb.asFloatBuffer();// 從字節緩衝區建立浮點緩衝區
vertexBuffer.put(sCoo);// 將座標添加到FloatBuffer
vertexBuffer.position(0);//設置緩衝區以讀取第一個座標
複製代碼

2.6: 繪製

繪製.png

public void draw() {
     // 將程序添加到OpenGL ES環境中
     GLES20.glUseProgram(mProgram);
     //獲取頂點着色器的vPosition成員的句柄
     mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
     //啓用三角形頂點的句柄
     GLES20.glEnableVertexAttribArray(mPositionHandle);
     //準備三角座標數據
     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//緩衝
     // 獲取片元着色器的vColor成員的句柄
     mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
     //爲三角形設置顏色
     GLES20.glUniform4fv(mColorHandle, 1, color, 0);
     //繪製三角形
     GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
     //禁用頂點數組:
     //禁用index指定的通用頂點屬性數組。
     // 默認狀況下,禁用全部客戶端功能,包括全部通用頂點屬性數組。
     // 若是啓用,將訪問通用頂點屬性數組中的值,
     // 並在調用頂點數組命令(如glDrawArrays或glDrawElements)時用於呈現
     GLES20.glDisableVertexAttribArray(mPositionHandle);
 }
複製代碼

副本二---龍之怒色

1.第一關卡:簡單認識OpenGL ES 着色腳本語言

GLSL(OpenGL Shader Language)數組

1.一種面相過程的高級語言
2.基於C/C++的語法(子集)及流程控制
3.完美支持向量和矩陣的操做
4.經過類型限定符來管理輸入與輸出
複製代碼

1.1:文件的格式

沒有統一的拓展名,通過百度,感受這種方式比較符合個人審美
並且AndroidStudio支持這些拓展名,你都叫.glsl也能夠,能分清就像緩存

assert.png

.vert - 頂點着色器
.tesc - 曲面細分控制着色器
.tese - 曲面細分評估着色器
.geom - 幾何着色器
.frag - 片元着色器
.comp - 計算着色器
複製代碼

原生數據類型

標量:一維的數值操做bash

float   浮點型
bool    布爾型
int     整型
|--- 支持 8進制(0開頭)  16進制(0x開頭)
複製代碼

向量:儲存及操做 顏色、位置、紋理座標等微信

vec2    二維向量型-浮點型
vec3    三維向量型-浮點型
vec4    四維向量型-浮點型

ivec2    二維向量型-整型
ivec3    三維向量型-整型
ivec4    四維向量型-整型

bvec2    二維向量型-布爾型
bvec3    三維向量型-布爾型
bvec4    四維向量型-布爾型
複製代碼

矩陣:根據矩陣的運算進行變換操做app

mat2    2X2矩陣-浮點型
mat3    3X3矩陣-浮點型
mat4    4X4矩陣-浮點型
複製代碼

採樣器ide

sampler2D   二維紋理
sampler3D   三維紋理
samplerCube 立方貼圖紋理
複製代碼

結構體:例如

struct ball{
    vec3 color;
    vec3 position;
}
複製代碼

數組

vec3 pos[]; //聲明不定大小的三維向量數組
vec3 pos[6];//聲明6個三維向量數組
複製代碼

限定符
attribute 頂點的變量,如頂點位置,顏色
uniform 
varying 用於從定點着色器傳遞到片元做色器的變量
const 
precision 精度
|---lowp
|---mediump
|---highp
複製代碼

2.第二關卡:資源文件的讀取

加載着色腳本的代碼差很少,封裝一下,寫個GLUtils吧:

/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/10 0010:10:58<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:OpenGL ES 輔助工具
 */
public class GLUtils {

    //從腳本中加載shader內容的方法
    public static int loadShaderAssets(Context ctx, int type, String name) {
        String result = null;
        try {
            InputStream in = ctx.getAssets().open(name);
            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 loadShader(type, result);
    }

    /**
     * 加載做色器
     *
     * @param type       着色器類型    頂點着色 {@link GLES20.GL_VERTEX_SHADER}
     *                   片元着色 {@link GLES20.GL_FRAGMENT_SHADER}
     * @param shaderCode 着色代碼
     * @return 做色器
     */
    public static int loadShader(int type, String shaderCode) {
        int shader = GLES20.glCreateShader(type);//建立着色器
        if (shader == 0) {//加載失敗直接返回
            return 0;
        }
        GLES20.glShaderSource(shader, shaderCode);//加載着色器源代碼
        GLES20.glCompileShader(shader);//編譯
        return checkCompile(type, shader);
    }

    /**
     * 檢查shader代碼是否編譯成功
     *
     * @param type   着色器類型
     * @param shader 着色器
     * @return 着色器
     */
    private static int checkCompile(int type, int shader) {
        int[] compiled = new int[1];//存放編譯成功shader數量的數組
        //獲取Shader的編譯狀況
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {//若編譯失敗則顯示錯誤日誌並
            Log.e("ES20_COMPILE_ERROR",
                    "Could not compile shader " + type + ":" + GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);//刪除此shader
            shader = 0;
        }
        return shader;
    }
}
複製代碼

3.第三關卡:tri.fragtri.vert的分析
3.1:先看片元:tri.frag

第一句是聲明片元的精度
第二句是聲明片元的顏色:一個vec4的變量--vColor
gl_FragColor = vColor; gl_FragColor是gl內定名,將vColor值賦給它

precision mediump float;
uniform vec4 vColor;
void main() {
  gl_FragColor = vColor;
}
複製代碼

單看一下着色的操做流程:

片元的着色.png

因此從Java代碼來看,重點在color,它是一個四值數組,每一個值0~1
分別對應r,g,b,a四值,即紅,綠,藍,透明四個顏色維度

// 顏色,rgba
float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
複製代碼

更換顏色:
rgba 132,197,240,255---->0.5176471f, 0.77254903f, 0.9411765f, 1.0f

更換顏色.png


3.2:再看定點:tri.vert

定義了一個四維的向量給gl_Position

attribute vec4 vPosition;
void main() {
  gl_Position = vPosition;
}
複製代碼

關於頂點的緩衝 初始化階段將頂點數據通過基本處理

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

/**
 * 緩衝數據
 */
private void bufferData() {
    ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每一個浮點數:座標個數* 4字節
    bb.order(ByteOrder.nativeOrder());//使用本機硬件設備的字節順序
    vertexBuffer = bb.asFloatBuffer();// 從字節緩衝區建立浮點緩衝區
    vertexBuffer.put(sCoo);// 將座標添加到FloatBuffer
    vertexBuffer.position(0);//設置緩衝區以讀取第一個座標
}
複製代碼

每三個數是一個頂點,分別表明(x,y,z),先卡z=0,也就是二維座標系
通過三個點的測試,能夠發現是一箇中心在原點,左右跨度爲1的座標系

座標系(二維).png

變更座標

變更座標.png


4.第三關卡:頂點着色

剛纔是給片元進行着色的,如今看看怎麼給頂點着色,確定要有頂點變量
前面關於修飾關鍵字:varying 用於從定點着色器傳遞到片元做色器的變量

4.1:頂點代碼:tri.vert
attribute vec3 vPosition;//頂點座標
uniform mat4 uMVPMatrix; //總變換矩陣
attribute vec4 aColor;//頂點顏色
varying  vec4 vColor;//片元顏色

void main() {
  gl_Position = uMVPMatrix*vec4(vPosition,1);
  vColor = aColor;//將頂點顏色傳給片元
}
複製代碼

4.2:片元代碼:tri.frag
precision mediump float;
varying vec4 vColor;
void main() {
  gl_FragColor = vColor;
}
複製代碼

4.3:使用:Triangle.java

三個點,第三個顏色,頂點+緩衝,跟頂點座標一個套路,取黃、藍、綠三色

//成員變量
private FloatBuffer mColorBuffer;//顏色緩衝
static final int COLOR_PER_VERTEX = 4;//向量維度
private final int vertexColorStride = COLOR_PER_VERTEX * 4; // 4*4=16
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//綠
};

//注意顏色句柄不是uniform了,獲取片元着色器的vColor成員的句柄
mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");

//啓用三角形頂點顏色的句柄
GLES20.glEnableVertexAttribArray(mColorHandle);

//準備三角頂點顏色數據
GLES20.glVertexAttribPointer(
                mColorHandle,
                COLOR_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexColorStride,
                mColorBuffer);  
複製代碼

頂點着色.png


副本三---龍之赤瞳

先看這個圖,按這樣來畫我的臉,豈不是會扁掉?這怎麼能忍

座標系(二維).png


1.第一關卡:相機--Matrix.setLookAtM

一共11個參數,嚇得我一抖,通過百度,再加上我神級的Ps技能,繪圖以下
主要有三個點eye(相機/眼睛位置),center(觀察物的位置),up(擡頭的感受,意會一下...)

setLookAtM.png

public static void setLookAtM(float[] rm, int rmOffset,
            float eyeX, float eyeY, float eyeZ,
            float centerX, float centerY, float centerZ, 
            float upX, float upY,float upZ) {
複製代碼

2.第二關卡:透視投影--Matrix.frustumM

八個參數,還好還好,也不是太多...

frustumM.png

Matrix.frustumM(float[] m, int offset,
            float left, float right, float bottom, float top,
            float near, float far)
複製代碼

3.第三關卡:修正視野,讓x,y看起來一致

修正視野.png

3.1.GLRenderer中:
//Model View Projection Matrix--模型視圖投影矩陣
private final float[] mMVPMatrix = new float[16];
//投影矩陣 mProjectionMatrix
private final float[] mProjectionMatrix = new float[16];
//視圖矩陣 mViewMatrix
private final float[] mViewMatrix = new float[16];

---->[GLRenderer#onSurfaceChanged]-------
float ratio = (float) width / height;
//透視投影矩陣--截錐
Matrix.frustumM(mProjectionMatrix, 0,
        -ratio, ratio, -1, 1, 
        3, 7);
// 設置相機位置(視圖矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
        0, 0, -3,
        0f, 0f, 0f,
        0f, 1.0f, 0.0f);
        
---->[GLRenderer#onDrawFrame]------- 

 // 計算投影和視圖轉換
 Matrix.multiplyMM(
         mMVPMatrix, 0,
         mProjectionMatrix, 0,
         mViewMatrix, 0);
 mTriangle.draw(mMVPMatrix);
複製代碼

3.2:tri.vert:爲頂點添加矩陣變換
attribute vec3 vPosition;//頂點座標
uniform mat4 uMVPMatrix; //總變換矩陣
void main() {
  gl_Position = uMVPMatrix*vec4(vPosition,1);
}
複製代碼

3.3:獲取句柄,修正頂點:Triangle.java
//獲取程序中總變換矩陣uMVPMatrix成員的句柄
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

---->[Triangle#draw]-------------
//對頂點進行矩陣變換
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0);
複製代碼

修正完畢.png


副本四--龍之振翼

1.第一關卡:旋轉30°

mMVPMatrix再進行矩陣變換就好了

旋轉30度.png

//變換矩陣
private float[] mOpMatrix = new float[16];

---->[GLRenderer#onDrawFrame]------- 
//mOpMatrix旋轉變換
Matrix.setRotateM(mOpMatrix, 0, 30, 0, 0, -1);

//使用mOpMatrix對mMVPMatrix進行變換
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mViewMatrix, 0,
        mOpMatrix, 0);
        
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mProjectionMatrix, 0,
        mMVPMatrix, 0);
複製代碼

隱藏關卡--Matrix.multiplyMM

我知道你看得一臉懵X,如今看看multiplyMM是個什麼東西
怎麼看?固然先看源碼啦,這是目前OpenGl ES 裏我見過註釋最多的...

將兩個4x4矩陣相乘,並將結果存儲在第三個4x4矩陣中。其中:result = lhs x rhs。
因爲矩陣相乘的工做方式,結果矩陣的效果至關於先被右邊的矩陣乘,再被左邊的矩陣乘。
這跟你指望的狀況是相反的。

result  保存結果的浮點數組
lhs     保存左側矩陣的浮點數組。
rhs     保存右側矩陣的浮點數組。

三個對應的offset--偏移量

public static native void multiplyMM(float[] result, int resultOffset,
        float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);
複製代碼

這裏都是用16個float的數組成的矩陣,寫個方法打印出來再說

打印矩陣.png

public static void logM(float[] matrix) {
    logM(matrix, "Matrix");
}
/**
 * 打印方陣數組
 *
 * @param matrix 
 * @param name
 */
public static void logM(float[] matrix, String name) {
    int wei = (int) Math.sqrt(matrix.length);
    StringBuffer sb = new StringBuffer("\n[");
    for (int i = 0; i < matrix.length; i++) {
        sb.append(matrix[i]);
        if ((i + 1) % wei == 0) {
            if (i == matrix.length - 1) {
                sb.append("]");
                continue;
            }
            sb.append("\n");
            continue;
        }
        sb.append(" , ");
    }
    Log.e("Matrix_TAG", name + ": " + sb.toString());
}
複製代碼

multiplyMM.png

multiplyMM


如今回頭再來看看:
mOpMatrix原本全是0,通過setRotateM以後變成圖中第一個矩陣
第一個Matrix.multiplyMMmOpMatrix矩陣做用於mViewMatrix上,得到結果矩陣:mMVPMatrix
第二個Matrix.multiplyMMmMVPMatrix矩陣做用於mProjectionMatrix上,得到結果矩陣:mMVPMatrix
最後根據頂點變換矩陣的句柄,將mMVPMatrix在tri.vert中做用在頂點上

//變換矩陣
private float[] mOpMatrix = new float[16];

//mOpMatrix旋轉變換
Matrix.setRotateM(mOpMatrix, 0, 30, 0, 0, -1);

//使用mOpMatrix對mMVPMatrix進行變換
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mViewMatrix, 0,
        mOpMatrix, 0);
        
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mProjectionMatrix, 0,
        mMVPMatrix, 0);
複製代碼

2.第二關卡:不停旋轉

當GLSurfaceView的setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);時
RendereronDrawFrame(GL10 gl) {會不斷執行,更新的時間間隔和手機有關
個人真機在13~19ms之間,模擬器在16~48ms之間,看了一下,轉一圈用6s,
即6000ms,一共360°,每次+1°,使用平均每度(每次刷新)用了16.667ms,好吧,完美的60fps

轉一圈.png

旋轉.gif

private int currDeg = 0;

---->[GLRenderer#onDrawFrame]------- 
//初始化變換矩陣
Matrix.setRotateM(mOpMatrix, 0, currDeg, 0, 0, -1);
Matrix.multiplyMM(mMVPMatrix, 0,
        mViewMatrix, 0,
        mOpMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0,
        mProjectionMatrix, 0,
        mMVPMatrix, 0);
mTriangle.draw(mMVPMatrix);

currDeg++;
if (currDeg == 360) {
    currDeg = 0;
}

複製代碼

3.第二關卡:不停旋轉着縮小

你拍照的時候怎麼讓成像縮小?----日後退唄!
根據後退爲正,能夠推測出座標系是一個右手系,也就是z軸朝向咱們
執行很簡單:Matrix.translateM 就能夠將mOpMatrix進行平移操做
以咱們的視角(參考系):你能夠想象成圖形(觀察物)一邊旋轉一邊原離咱們,也能夠反過來想一想

引擎推進的不是飛船而是宇宙。飛船壓根就沒動過。--若是對矩陣有興趣,建議把這篇看十遍

//設置沿Z軸位移
Matrix.translateM(mOpMatrix, 0, 0, 0, currDeg/90.f);
複製代碼

旋轉+縮小.gif


NPC: 恭喜您,完成第四副本,如今您得到OpenGL-ES 新手戰士的稱號,請留下名號:
我(輸入):張風捷特烈
NPC: 張風捷特烈,是否繼續前行,下面的關卡將更加艱難
我:(點擊肯定) 執劍向前
NPC: 尊敬的勇者-張風捷特烈,祝您一路平安,成功斬殺黑龍...

第一集結束,下一集:"謎團立方" 敬請期待

後記:捷文規範

1.本文成長記錄及勘誤表
項目源碼 日期 備註
V0.1-github 2018-1-11 Android多媒體之GL-ES戰記第一集--勇者集結
2.更多關於我
筆名 QQ 微信 愛好
張風捷特烈 1981462002 zdl1994328 語言
個人github 個人簡書 個人掘金 我的網站

本文參考:
1.《Android 3D遊戲開發技術寶典 OpenGL ES 2.0》
2.OpenGL ES 學習記錄
3.opengl-tutorial:OpenGL基礎知識
4.廣大網友的文章零散參考,就不一一列舉了

icon_wx_200.png
相關文章
相關標籤/搜索