Android多媒體之GLES2戰記第三集--聖火之光

前情回顧

旁邊: 勇者們爲求黑龍寶藏,集結起來共闖黑龍副本,經歷重重艱辛,
終於得到立方開啓了黑龍之門,這也只是新徵程的起點,後面將有更大的挑戰等着他們
張風捷特烈打開了門以後,看到了什麼?讓咱們繼續收看java


副本九:黑暗之淵

在打開門後,光芒所有消失,眼中一團黑暗,張風捷特烈踏出一步
便馬上下墜,彷彿是無盡的深淵,地面?地面在那裏?我還要下墜多久?git

world-black.png


1.第一關卡:創造世界

NPC:This is the world without anything,you must create everything by yourself.
我:好吧,總結一下流程吧,順便該封的封一下github

簡單的世界.png

1.1.常量:
public class Cons {
    //維度:獨立參數的數目
    public static final int DIMENSION_2 = 2;//2維度
    public static final int DIMENSION_3 = 3;//3維度
    public static final int DIMENSION_4 = 4;//4維度
}
複製代碼

1.2.顯示的世界:World.java
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/13/013:10:46<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:GL的世界
 */
public class World extends GLSurfaceView {
    private WorldRenderer mRenderer;
    public World(Context context) {
        this(context,null);
    }
    public World(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private void init() {
        setEGLContextClientVersion(2);//設置OpenGL ES 2.0 context
        mRenderer = new WorldRenderer(getContext());
        setRenderer(mRenderer);//設置渲染器
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }
}
複製代碼

1.3.世界的渲染器WorldRenderer
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/9 0009:18:56<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:GL世界渲染類
 */
public class WorldRenderer implements GLSurfaceView.Renderer {
    private static final String TAG = "GLRenderer";
    //Model View Projection Matrix--模型視圖投影矩陣
    private static float[] mMVPMatrix = new float[16];
    //投影矩陣 mProjectionMatrix
    private static final float[] mProjectionMatrix = new float[16];
    //視圖矩陣 mViewMatrix
    private static final float[] mViewMatrix = new float[16];
    //變換矩陣
    private float[] mOpMatrix = new float[16];
    private Context mContext;
    private RendererAble mWorldShape;
    public WorldRenderer(Context context) {
        mContext = context;
    }
    private int currDeg = 0;
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(0.0f,0.0f,0.0f,1.0f);//rgba
        mWorldShape = new WorldShape(mContext);
    }
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);//GL視口
        float ratio = (float) width / height;
        //透視投影矩陣--截錐
        Matrix.frustumM(mProjectionMatrix, 0,
                -ratio, ratio, -1, 1,
                3, 9);
        // 設置相機位置(視圖矩陣)
        Matrix.setLookAtM(mViewMatrix, 0,
                2f, 2f, -6.0f,
                0f, 0f, 0f,
                0f, 1.0f, 0.0f);
    }
    /**
     * 此方法會不斷執行 {@link GLSurfaceView.RENDERMODE_CONTINUOUSLY}
     * 此方法執行一次 {@link GLSurfaceView.RENDERMODE_WHEN_DIRTY}
     *
     * @param gl
     */
    @Override
    public void onDrawFrame(GL10 gl) {
        //清除顏色緩存和深度緩存
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        //初始化變換矩陣
        Matrix.setRotateM(mOpMatrix, 0, currDeg, 0, 1, 0);
        Matrix.multiplyMM(mMVPMatrix, 0,
                mViewMatrix, 0,
                mOpMatrix, 0);
        Matrix.multiplyMM(mMVPMatrix, 0,
                mProjectionMatrix, 0,
                mMVPMatrix, 0);
        mWorldShape.draw(mMVPMatrix);
        //打開深度檢測
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
    }
}
複製代碼

2.第二關卡:打開聖火之光(畫點)

黑暗中應該先出現一個點,表明但願之光編程

一點.png


2.1--片元着色代碼:world.frag
precision mediump float;
 varying vec4 vColor;
 
 void main() {
   gl_FragColor = vColor;
 }
複製代碼

2.2--頂點着色代碼:world.frag

注意這裏要設置點的大小,不然默認爲0緩存

attribute vec3 vPosition;//頂點座標
uniform mat4 uMVPMatrix; //總變換矩陣
attribute vec4 aColor;//頂點顏色
varying  vec4 vColor;//片元顏色

void main() {
  gl_Position = uMVPMatrix*vec4(vPosition,1);
  vColor = aColor;//將頂點顏色傳給片元
  gl_PointSize=10.0;//設置點的大小,默認爲0
}
複製代碼

2.3--點形狀繪製
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/13/013:8:39<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:世界的形狀
 */
public class WorldShape extends RendererAble {
    private int mProgram;//OpenGL ES 程序
    private int mPositionHandle;//位置句柄
    private int mColorHandle;//顏色句柄
    private int muMVPMatrixHandle;//頂點變換矩陣句柄
    private FloatBuffer mColorBuffer;//顏色緩衝
    private final int vertexColorStride = Cons.DIMENSION_4 * 4; // 4*4=16
    private FloatBuffer mVertexBuffer;//頂點緩衝
    private final int vertexStride = Cons.DIMENSION_3 * 4; // 3*4=12
    private float[] mVertex = new float[]{
            0.0f,0.0f,0.0f
    };

    private float[] mColor = new float[]{
            1.0f, 1.0f, 1.0f, 1.0f,
    };

    public WorldShape(Context context) {
        super(context);
        mColorBuffer = GLUtil.getFloatBuffer(mColor);
        mVertexBuffer = GLUtil.getFloatBuffer(mVertex);
        initProgram();
    }

    private void initProgram() {
        //頂點着色
        int vertexShader = GLUtil.loadShaderAssets(mContext,
                GLES20.GL_VERTEX_SHADER, "world.vert");
        //片元着色
        int fragmentShader = GLUtil.loadShaderAssets(mContext,
                GLES20.GL_FRAGMENT_SHADER, "world.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, "aColor");
        //獲取程序中總變換矩陣uMVPMatrix成員的句柄
        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    }

    @Override
    public void draw(float[] mvpMatrix) {
        // 將程序添加到OpenGL ES環境中
        GLES20.glUseProgram(mProgram);
        //啓用頂點的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //啓用頂點顏色的句柄
        GLES20.glEnableVertexAttribArray(mColorHandle);
        //頂點矩陣變換
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0);
        //準備頂點座標數據
        GLES20.glVertexAttribPointer(
                mPositionHandle,//int indx, 索引
                Cons.DIMENSION_3,//int size,大小
                GLES20.GL_FLOAT,//int type,類型
                false,//boolean normalized,//是否標準化
                vertexStride,// int stride,//跨度
                mVertexBuffer);// java.nio.Buffer ptr//緩衝
        //準備頂點顏色數據
        GLES20.glVertexAttribPointer(
                mColorHandle,
                Cons.DIMENSION_4,
                GLES20.GL_FLOAT,
                false,
                vertexColorStride,
                mColorBuffer);
        int count = mVertex.length / Cons.DIMENSION_3;
        GLES20.glDrawArrays(GLES20.GL_POINTS, 0, count);
    }
}
複製代碼

NPC:很好,獲取技能GLES20.GL_POINTS,勇者,繼續展示你的創造力吧!bash


3.第三關卡:繪製四點

四點.png

private float[] mVertex = new float[]{
        -1.0f, 0.0f, -1.0f,
        -1.0f, 0.0f, 1.0f,
        1.0f, 0.0f, 1.0f,
        1.0f, 0.0f, -1.0f,
};

private float[] mColor = new float[]{
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
};
複製代碼

張風捷特烈黑暗之淵中踩在四個點上,中止了下落,通過測量,發現點的單位是px微信

通過ps的精確測量10px.png


副本十:縈龍之絲

1.第一關卡:座標系體系

接下來咱們將使用如下視角進行世界的構建ide

World.png

如今將D點變色:可見視角和座標系不一致post

如今的視角.png

private float[] mVertex = new float[]{
        -1.0f, 0.0f, -1.0f,//A
        -1.0f, 0.0f, 1.0f,//B
        1.0f, 0.0f, 1.0f,//C
        1.0f, 0.0f, -1.0f,//D
};
private float[] mColor = new float[]{
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        0.21960784f,0.56078434f,0.92156863f,1.0f,
};
複製代碼

2.第二關卡:調整視角,符合ps畫的座標系

爲了視覺上好些,也爲了ps裏畫圖方便,這裏講視角逆時針旋轉130°網站

旋轉視角.png

Matrix.setRotateM(mOpMatrix, 0, currDeg+130, 0, 1, 0);
複製代碼

點的旋轉.gif


3.第三關卡:畫線

直接把畫點改爲畫線就好了,看一下GLES20幾個常量的區別

畫線

GLES20.glLineWidth(10);//設置線的寬度
int count = mVertex.length / Cons.DIMENSION_3;
//GLES20.glDrawArrays(GLES20.GL_POINTS, 0, count);
//GLES20.glDrawArrays(GLES20.GL_LINES, 0, count);
//GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 0, count);
GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, count);
複製代碼

旋轉線


爲了使用方便,封裝一下繪製簡單圖形的代碼,就是把變量抽取一下
雖然只能畫些簡單的東西,但畫畫輔助線仍是蠻方便的,一個SimpleShape

/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/13/013:17:37<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:形狀類
 */
public class Shape {
    private float[] mVertex;//頂點
    private float[] mColor;//顏色
    private int mDrawType;//繪製類型
複製代碼
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/13/013:8:39<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:簡單的形狀
 */
public class SimpleShape extends RendererAble {
    //略...
    private Shape mShape;

    public SimpleShape(Context context, Shape shape) {
        super(context);
        mShape = shape;
        mColorBuffer = GLUtil.getFloatBuffer(mShape.getColor());
        mVertexBuffer = GLUtil.getFloatBuffer(mShape.getVertex());
        initProgram();
    }
    //略...
複製代碼

副本十一:The World

目的,形象地認識這個世界


1.第一關卡:座標系的繪製
1.1:肯定座標和顏色(因爲不怎麼變更,因此放在常量類Cons裏了)

記住三個軸的顏色(Z軸:藍色,X軸:黃色,Y軸:綠色)

世界座標系.png

public static final float[] VERTEX_COO = {//座標軸
        0.0f, 0.0f, 0.0f,//Z軸
        0.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 0.0f,//X軸
        1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 0.0f,//Y軸
        0.0f, 1.0f, 0.0f,
};
public static final float[] COLOR_COO = {//座標軸顏色
        0.0f, 0.0f, 1.0f, 1.0f,//Z軸:藍色
        0.0f, 0.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 0.0f, 1.0f,//X軸:黃色
        1.0f, 1.0f, 0.0f, 1.0f,
        0.0f, 1.0f, 0.0f, 1.0f,//Y軸:綠色
        0.0f, 1.0f, 0.0f, 1.0f,
};
複製代碼

1.2:使用SimpleShape
---->[WorldRenderer#onSurfaceCreated]--------
Shape shape = new Shape(Cons.VERTEX_COO, Cons.COLOR_COO, GLES20.GL_LINES);
mCoo = new SimpleShape(mContext, shape);

---->[WorldRenderer#onDrawFrame]--------
mCoo.draw(mMVPMatrix);
複製代碼

world.gif


2.第二關卡:簡單封裝

若是圖形建立在WorldRenderer中,感受很不舒服,畢竟會有不少形狀,
WorldRenderer的本意只是爲了渲染以及視角的控制,並不但願圖形摻雜其中
WorldShape能夠專門繪製形狀,由它統一貫WorldRenderer輸出形狀
既然WorldShape總管圖形,那麼操做圖形,在所不免,建一個OP接口,目前只放兩個方法

簡單封裝.png


2.1:操做接口
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/13/013:19:27<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:操做接口
 */
public interface OP<T> {
    /**
     * 添加
     * @param ts 若干對象
     */
    void add(T... ts);

    /**
     * 根據id移除元素
     * @param id 索引
     */
    void remove(int id);
}

複製代碼

2.2:世界的形狀:WorldShape
/**
 * 做者:張風捷特烈<br/>
 * 時間:2019/1/13/013:8:39<br/>
 * 郵箱:1981462002@qq.com<br/>
 * 說明:世界的形狀
 */
public class WorldShape extends RendererAble implements OP<RendererAble>{
    List<RendererAble> mRendererAbles;
    private float[] mVertex = new float[]{
            -1.0f, 0.0f, -1.0f,//A
            -1.0f, 0.0f, 1.0f,//B
            1.0f, 0.0f, 1.0f,//C
            1.0f, 0.0f, -1.0f,//D
    };
    private float[] mColor = new float[]{
            1.0f, 1.0f, 1.0f, 1.0f,
            1.0f, 1.0f, 1.0f, 1.0f,
            1.0f, 1.0f, 1.0f, 1.0f,
            0.21960784f, 0.56078434f, 0.92156863f, 1.0f,
    };
    public WorldShape(Context ctx) {
        super(ctx);
        mRendererAbles = new ArrayList<>();

        Shape coo = new Shape(Cons.VERTEX_COO, Cons.COLOR_COO, GLES20.GL_LINES);
        Shape ground = new Shape(mVertex, mColor, GLES20.GL_LINE_LOOP);
        add(
                new SimpleShape(mContext,coo),
                new SimpleShape(mContext,ground),
    }
    @Override
    public void draw(float[] mvpMatrix) {
        for (RendererAble rendererAble : mRendererAbles) {
            rendererAble.draw(mvpMatrix);
        }
    }
    @Override
    public void add(RendererAble... rendererAbles) {
        for (RendererAble rendererAble : rendererAbles) {
            mRendererAbles.add(rendererAble);
        }
    }
    @Override
    public void remove(int id) {
        if (id>=mRendererAbles.size()) {
            return;
        }
        mRendererAbles.remove(id);
    }
}
複製代碼

2.3:使用WorldShape

如今工做重心移入WorldShape,避免對WorldRenderer形成負擔

---->[WorldRenderer#onSurfaceCreated]--------
mWorldShape = new WorldShape(mContext);

---->[WorldRenderer#onDrawFrame]--------
 mWorldShape.draw(mMVPMatrix);
複製代碼

3.Shape的強化,移動與移動建立

關於深拷貝和淺拷貝我就不廢話了,移動建立中須要深拷貝(成員變量有引用數據類型)
Shape implements Cloneable

3.1:深拷貝
/**
 * 深拷貝
 * @return 形狀副本
 */
public Shape clone() {
    Shape clone = null;
    try {
        clone = (Shape) super.clone();
        float[] vertex = new float[mVertex.length];
        float[] color = new float[mColor.length];
        System.arraycopy(mVertex, 0, vertex, 0, mVertex.length);
        System.arraycopy(mColor, 0, color, 0, mColor.length);
        clone.mVertex = vertex;
        clone.mColor = color;
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return clone;
}
複製代碼

3.2:移動與移動拷貝
/**
 * 移動並建立新圖形
 * @param x
 * @param y
 * @param z
 * @return
 */
public Shape moveAndCreate(float x, float y, float z) {
    Shape clone = clone();
    clone.move(x, y, z);
    return clone;
}

/**
 * 僅移動圖形
 * @param x
 * @param y
 * @param z
 */
public void move(float x, float y, float z) {
    for (int i = 0; i < mVertex.length; i++) {
        if (i % 3 == 0) {//x
            mVertex[i] += x;
        }
        if (i % 3 == 1) {//y
            mVertex[i] += y;
        }
        if (i % 3 == 2) {//y
            mVertex[i] += z;
        }
    }
}
複製代碼

3.3:移動建立圖形

兩行代碼搞定,我都佩服我本身,感受能夠用矩陣變換,如今還不是進擊矩陣的時候

移動複製.gif

---->[WorldShape#WorldShape]------------
 Shape coo = new Shape(Cons.VERTEX_COO, Cons.COLOR_COO, GLES20.GL_LINES);
 Shape ground = new Shape(mVertex, mColor, GLES20.GL_LINE_LOOP);
 Shape top = ground.moveAndCreate(0, 1, 0);
 Shape bottom = ground.moveAndCreate(0, -1, 0);
 add(
         new SimpleShape(mContext,coo),
         new SimpleShape(mContext,top),
         new SimpleShape(mContext,bottom),
         new SimpleShape(mContext,ground));
複製代碼

3.4:再加四根線(感受有點low...)
private float[] mVertex2 = new float[]{
        1.0f, 1.0f, 1.0f,
        1.0f, -1.0f, 1.0f,

        -1.0f, 1.0f, 1.0f,
        -1.0f, -1.0f, 1.0f,

        -1.0f, 1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,

        1.0f, 1.0f, -1.0f,
        1.0f, -1.0f, -1.0f,
};
private float[] mColor2 = new float[]{
        1.0f, 0.0f, 0.0f, 1.0f,
        1.0f, 0.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
};

Shape side = new Shape(mVertex2, mColor2, GLES20.GL_LINES);
複製代碼

線立方.gif

世界的座標已經映入眼簾,yes!


副本十二:黑龍之瞳LEVEL 2

在明確世界座標以後,如今能夠再來看一下視線了
相信你會以爲恍然大悟,原來如此,just so so
在此以前再說一遍:Z軸:藍色,X軸:黃色,Y軸:綠色,正對紅線


1.第一關卡:移動相機 Z軸

注意:如今將視角轉回(0,0,-6),旋轉角度歸0爲了避免遮擋視線,將ground四條線隱藏
看紅線在後面,說明咱們是從後面開始看的,Z軸:藍色沒法看到,說明視點在Z軸
即:如今視點在Z軸上,值爲-6,絕對值的大小即離物體的遠近,近大遠小沒毛病
but,移到-8時,可見後面已經消失了,說明視野是有限制的

視線點

// 設置相機位置(視圖矩陣)
    Matrix.setLookAtM(mViewMatrix, 0,
        0f, 0f, -6.0f,
        0f, 0f, 0f,
        0f, 1.0f, 0.0f);
複製代碼

2.第二關卡:移動相機 X軸

將X每次向x負方向移動0.3f,想一下你拿着相機站在後面,看你的X軸方向
或者直接看黃線,黃線所指方向爲X軸正方向,你應該能夠感受相機是怎麼移動的吧!

x軸轉

// 設置相機位置(視圖矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
        -1.5f, 0f, -6,
        0f, 0f, 0f,
        0f, 1.0f, 0.0f);
複製代碼

3.第三關卡:移動相機 Y軸

將Y每次向Y負方向移動0.3f,想一下你拿着相機站在後面,看你的X軸方向
或者直接看黃線,黃線所指方向爲X軸正方向,你應該能夠感受相機是怎麼移動的吧!

y軸轉.png

// 設置相機位置(視圖矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
        -1.5f, 1.5f, -6,
        0f, 0f, 0f,
        0f, 1.0f, 0.0f);
複製代碼

GLSurfaceView再怎麼牛,也是個View,咱們即可以添加事件
下面一個小練習,相信上面的理解了,對你來講不會太難

操做.gif


NPC:恭喜完成十二個新手副本,下面將進入普通副本,祝君順利

本集結束,下集--移形換影,敬請期待

後記:捷文規範

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

1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持


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