旁邊: 勇者們爲求黑龍寶藏,集結起來共闖黑龍副本,經歷重重艱辛,
終於得到立方開啓了黑龍之門
,這也只是新徵程的起點,後面將有更大的挑戰等着他們
張風捷特烈
打開了門以後,看到了什麼?讓咱們繼續收看java
黑暗之淵
在打開門後,光芒所有消失,眼中一團黑暗,
張風捷特烈
踏出一步
便馬上下墜,彷彿是無盡的深淵,地面?地面在那裏?我還要下墜多久?git
NPC:
This is the world without anything,you must create everything by yourself.
我:好吧,總結一下流程吧,順便該封的封一下github
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維度
}
複製代碼
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);
}
}
複製代碼
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);
}
}
複製代碼
黑暗中應該先出現一個點,表明但願之光編程
world.frag
precision mediump float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
複製代碼
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
}
複製代碼
/**
* 做者:張風捷特烈<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
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微信
縈龍之絲
接下來咱們將使用如下視角進行世界的構建ide
如今將D點變色:可見視角和座標系不一致post
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,
};
複製代碼
爲了視覺上好些,也爲了ps裏畫圖方便,這裏講視角逆時針旋轉130°網站
Matrix.setRotateM(mOpMatrix, 0, currDeg+130, 0, 1, 0);
複製代碼
直接把畫點改爲畫線就好了,看一下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
目的,形象地認識這個世界
記住三個軸的顏色(
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,
};
複製代碼
---->[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);
複製代碼
若是圖形建立在
WorldRenderer
中,感受很不舒服,畢竟會有不少形狀,
WorldRenderer
的本意只是爲了渲染以及視角的控制,並不但願圖形摻雜其中
WorldShape
能夠專門繪製形狀,由它統一貫WorldRenderer輸出形狀
既然WorldShape
總管圖形,那麼操做圖形,在所不免,建一個OP接口,目前只放兩個方法
操做接口
/**
* 做者:張風捷特烈<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);
}
複製代碼
世界的形狀: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);
}
}
複製代碼
如今工做重心移入WorldShape,避免對WorldRenderer形成負擔
---->[WorldRenderer#onSurfaceCreated]--------
mWorldShape = new WorldShape(mContext);
---->[WorldRenderer#onDrawFrame]--------
mWorldShape.draw(mMVPMatrix);
複製代碼
關於深拷貝和淺拷貝我就不廢話了,移動建立中須要深拷貝(成員變量有引用數據類型)
Shape implements Cloneable
/**
* 深拷貝
* @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;
}
複製代碼
/**
* 移動並建立新圖形
* @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;
}
}
}
複製代碼
兩行代碼搞定,我都佩服我本身,感受能夠用矩陣變換,如今還不是進擊矩陣的時候
---->[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));
複製代碼
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軸:綠色
,正對紅線
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);
複製代碼
X軸
將X每次向x負方向移動0.3f,想一下你拿着相機站在後面,看你的X軸方向
或者直接看黃線,黃線所指方向爲X軸正方向,你應該能夠感受相機是怎麼移動的吧!
// 設置相機位置(視圖矩陣)
Matrix.setLookAtM(mViewMatrix, 0,
-1.5f, 0f, -6,
0f, 0f, 0f,
0f, 1.0f, 0.0f);
複製代碼
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:恭喜完成十二個
新手副本
,下面將進入普通副本
,祝君順利
本集結束,下集--移形換影,敬請期待
項目源碼 | 日期 | 備註 |
---|---|---|
V0.1-github | 2018-1-14 | Android多媒體之GL-ES戰記第三集--聖火之光 |
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
個人github | 個人簡書 | 個人掘金 | 我的網站 |
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持