前情回顧
旁邊: 勇者們為求黑龍寶藏,集結起來共闖黑龍副本,經歷重重艱辛,
終於獲得立方開啟了黑龍之門
,這也只是新徵程的起點,後面將有更大的挑戰等著他們
張風捷特烈
開啟了門之後,看到了什麼?讓我們繼續收看
副本九:黑暗之淵
在開啟門後,光芒全部消失,眼中一團黑暗,
張風捷特烈
踏出一步
便立刻下墜,彷彿是無盡的深淵,地面?地面在那裡?我還要下墜多久?
1.第一關卡:創造世界
NPC:
This is the world without anything,you must create everything by yourself.
我:好吧,總結一下流程吧,順便該封的封一下
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.第二關卡:開啟聖火之光(畫點)
黑暗中應該先出現一個點,代表希望之光
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
,勇者,繼續展現你的創造力吧!
3.第三關卡:繪製四點
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
副本十:縈龍之絲
1.第一關卡:座標系體系
接下來我們將使用以下視角進行世界的構建
現在將D點變色:可見視角和座標系不一致
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°
Matrix.setRotateM(mOpMatrix, 0, currDeg+130, 0, 1, 0);
複製程式碼
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軸:綠色
)
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);
複製程式碼
2.第二關卡:簡單封裝
如果圖形建立在
WorldRenderer
中,感覺很不舒服,畢竟會有很多形狀,
WorldRenderer
的本意只是為了渲染以及視角的控制,並不希望圖形摻雜其中
WorldShape
可以專門繪製形狀,由它統一向WorldRenderer輸出形狀
既然WorldShape
總管圖形,那麼操作圖形,在所難免,建一個OP介面,目前只放兩個方法
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:移動建立圖形
兩行程式碼搞定,我都佩服我自己,感覺可以用矩陣變換,現在還不是進擊矩陣的時候
---->[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);
複製程式碼
世界的座標已經映入眼簾,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軸正方向,你應該可以感覺相機是怎麼移動的吧!
// 設定相機位置(檢視矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
-1.5f, 0f, -6,
0f, 0f, 0f,
0f, 1.0f, 0.0f);
複製程式碼
3.第三關卡:移動相機 Y軸
將Y每次向Y負方向移動0.3f,想一下你拿著相機站在後面,看你的X軸方向
或者直接看黃線,黃線所指方向為X軸正方向,你應該可以感覺相機是怎麼移動的吧!
// 設定相機位置(檢視矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
-1.5f, 1.5f, -6,
0f, 0f, 0f,
0f, 1.0f, 0.0f);
複製程式碼
GLSurfaceView再怎麼牛,也是個View,我們便可以新增事件
下面一個小練習,相信上面的理解了,對你來說不會太難
NPC:恭喜完成十二個
新手副本
,下面將進入普通副本
,祝君順利
本集結束,下集--移形換影,敬請期待
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1-github | 2018-1-14 | Android多媒體之GL-ES戰記第三集--聖火之光 |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的掘金 | 個人網站 |
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援