前情回顾
旁边: 勇者们为求黑龙宝藏,集结起来共闯黑龙副本,经历重重艰辛,
终于获得立方开启了黑龙之门
,这也只是新征程的起点,后面将有更大的挑战等着他们张风捷特烈
打开了门之后,看到了什么?让我们继续收看
副本九:黑暗之渊
在打开门后,光芒全部消失,眼中一团黑暗,
便立刻下坠,仿佛是无尽的深渊,地面?地面在那里?我还要下坠多久?张风捷特烈
踏出一步
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
/** * 作者:张风捷特烈 * 时间:2019/1/13/013:10:46 * 邮箱:1981462002@qq.com * 说明: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
/** * 作者:张风捷特烈 * 时间:2019/1/9 0009:18:56 * 邮箱:1981462002@qq.com * 说明: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--点形状绘制
/** * 作者:张风捷特烈 * 时间:2019/1/13/013:8:39 * 邮箱:1981462002@qq.com * 说明:世界的形状 */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
类
/** * 作者:张风捷特烈 * 时间:2019/1/13/013:17:37 * 邮箱:1981462002@qq.com * 说明:形状类 */public class Shape { private float[] mVertex;//顶点 private float[] mColor;//颜色 private int mDrawType;//绘制类型复制代码
/** * 作者:张风捷特烈 * 时间:2019/1/13/013:8:39 * 邮箱:1981462002@qq.com * 说明:简单的形状 */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:操作接口
/** * 作者:张风捷特烈 * 时间:2019/1/13/013:19:27 * 邮箱:1981462002@qq.com * 说明:操作接口 */public interface OP{ /** * 添加 * @param ts 若干对象 */ void add(T... ts); /** * 根据id移除元素 * @param id 索引 */ void remove(int id);}复制代码
2.2:世界的形状:WorldShape
/** * 作者:张风捷特烈 * 时间:2019/1/13/013:8:39 * 邮箱:1981462002@qq.com * 说明:世界的形状 */public class WorldShape extends RendererAble implements OP{ List 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.本文成长记录及勘误表
日期 | 备注 | |
---|---|---|
2018-1-14 |
2.更多关于我
笔名 | 微信 | 爱好 | |
---|---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 | 语言 |
3.声明
1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流 3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正 4----看到这里,我在此感谢你的喜欢与支持