内容简介:传说,在这片代码大陆上,存在一个古老的种族,它们拥有无尽的力量,却罕有人能够驾驭多媒体王国中存在一个隐蔽的角落,是这个种族的栖息之地,很少有人敢冒犯那里Android多媒体领域有一处:被后人称为
传说,在这片代码大陆上,存在一个古老的种族,它们拥有无尽的力量,却罕有人能够驾驭
多媒体王国中存在一个隐蔽的角落,是这个种族的栖息之地,很少有人敢冒犯那里
Android多媒体领域有一处:被后人称为 黑龙洞穴--OpenGL ES
,其中埋藏着图形界的无限财富
勇士们,举起手中的剑,进发!
副本一: 黑龙洞口
NPC:黑龙洞口一片漆黑,其中隐藏着什么规律,勇士们,一起寻找吧!
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中
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new GLView(this)); } } 复制代码
2.第二关卡:三角形的绘制
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:勇者,你阵亡了没...如果现在退出还来得及,这将是一篇宏伟的战斗史诗
如果你还想继续,举起你手中的剑,同我一起,进发!!!
/** * 加载作色器 * @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:渲染器程序
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:顶点缓冲
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: 绘制
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
也可以,能分清就像
.vert - 顶点着色器 .tesc - 曲面细分控制着色器 .tese - 曲面细分评估着色器 .geom - 几何着色器 .frag - 片元着色器 .comp - 计算着色器 复制代码
原生数据类型
标量:一维的数值操作
float 浮点型 bool 布尔型 int 整型 |--- 支持 8进制(0开头) 16进制(0x开头) 复制代码
向量:储存及操作 颜色、位置、纹理坐标等
vec2 二维向量型-浮点型 vec3 三维向量型-浮点型 vec4 四维向量型-浮点型 ivec2 二维向量型-整型 ivec3 三维向量型-整型 ivec4 四维向量型-整型 bvec2 二维向量型-布尔型 bvec3 三维向量型-布尔型 bvec4 四维向量型-布尔型 复制代码
矩阵:根据矩阵的运算进行变换操作
mat2 2X2矩阵-浮点型 mat3 3X3矩阵-浮点型 mat4 4X4矩阵-浮点型 复制代码
采样器
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.frag
和 tri.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; } 复制代码
单看一下着色的操作流程:
所以从 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
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的坐标系
变动坐标
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); 复制代码
副本三--- 龙之赤瞳
先看这个图,按这样来画个人脸,岂不是会扁掉?这怎么能忍
1.第一关卡:相机-- Matrix.setLookAtM
一共11个参数,吓得我一抖,经过百度,再加上我 神级的Ps技能
,绘图如下
主要有三个点eye(相机/眼睛位置),center(观察物的位置),up(抬头的感觉,意会一下...)
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
八个参数,还好还好,也不是太多...
Matrix.frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far) 复制代码
3.第三关卡:修正视野,让x,y看起来一致
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); 复制代码
副本四-- 龙之振翼
1.第一关卡:旋转30°
对 mMVPMatrix
再进行矩阵变换就行了
//变换矩阵 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的数组成的矩阵,写个方法打印出来再说
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()); } 复制代码
现在回头再来看看:
mOpMatrix本来全是0,经过setRotateM之后变成图中第一个矩阵
第一个Matrix.multiplyMM
将
mOpMatrix
矩阵作用于
mViewMatrix
上,获得结果矩阵:
mMVPMatrix
第二个
Matrix.multiplyMM
将
mMVPMatrix
矩阵作用于
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)
;时
Renderer
的 onDrawFrame(GL10 gl) {
会不断执行,更新的时间间隔和手机有关
我的真机在 13~19ms
之间,模拟器在 16~48ms
之间,看了一下,转一圈用6s,
即6000ms,一共360°,每次+1°,使用平均每度(每次刷新)用了16.667ms,好吧,完美的 60fps
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); 复制代码
NPC: 恭喜您,完成第四副本,现在您获得OpenGL-ES 新手战士的称号,请留下名号:
我(输入):张风捷特烈
NPC: 张风捷特烈,是否继续前行,下面的关卡将更加艰难
我:(点击确定) 执剑向前
NPC: 尊敬的勇者-张风捷特烈,祝您一路平安,成功斩杀黑龙...
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 开发人员称Switch《勇者斗恶龙11》仍需相当长时间制作
- WWDC 2017新品集结: 你最喜欢哪一款?
- 2019年谷安DevOps Master 北京上海深圳开班集结令!
- GeekPwn2018:全球顶尖黑客已集结,AI安全与每个人都有关
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编写可维护的JavaScript
扎卡斯 / 李晶、郭凯、张散集 / 人民邮电出版社 / 2013-4 / 55.00元
《编写可维护的JavaScript》向开发人员阐述了如何在团队开发中编写具备高可维护性的JavaScript代码,书中详细说明了作为团队一分子,应该怎么写JavaScript。《编写可维护的JavaScript》内容涵盖了编码风格、编程技巧、自动化、测试等几方面,既包括具体风格和原则的介绍,也包括示例和技巧说明,最后还介绍了如何通过自动化的工具和方法来实现一致的编程风格。 《编写可维护的Ja......一起来看看 《编写可维护的JavaScript》 这本书的介绍吧!