前言

1.本系列借花献佛,结合了很多前人的文章以及书籍,我尽可能去总结并用我的思想进行加工
2.OpenGL一直是我的心结,也是时候去解开了,本系列称不上原创,但每行代码都有着我思考的痕迹
3.本系列所有的图片都是[张风捷特烈]所画,如果有什么错误还请指出,定会最快改正
4.本系列文章允许转载、截取、公众号发布,请保留前言部分,希望广大读者悉心指教


NPC:开场词

传说,在这片代码大陆上,存在一个古老的种族,它们拥有无尽的力量,却罕有人能够驾驭
多媒体王国中存在一个隐蔽的角落,是这个种族的栖息之地,很少有人敢冒犯那里
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 contextmRenderer = 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 {@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES20.glViewport(0, 0, width, height);//GL视口}@Overridepublic void onDrawFrame(GL10 gl) {//清除颜色缓存和深度缓存GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);}
}
复制代码

1.3:Activity中
public class MainActivity extends AppCompatActivity {@Overrideprotected 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  // 右下};// 颜色,rgbafloat 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);// 将坐标添加到FloatBuffervertexBuffer.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;}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgbamTriangle = new Triangle();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES20.glViewport(0, 0, width, height);//GL视口}@Overridepublic 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);//删除此shadershader = 0;}return shader;}
}
复制代码

3.第三关卡:tri.fragtri.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);// 将坐标添加到FloatBuffervertexBuffer.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.multiplyMMmOpMatrix矩阵作用于mViewMatrix上,获得结果矩阵:mMVPMatrix
第二个Matrix.multiplyMMmMVPMatrix矩阵作用于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);时
RendereronDrawFrame(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: 尊敬的勇者-张风捷特烈,祝您一路平安,成功斩杀黑龙...

第一集结束,下一集:"谜团立方" 敬请期待

后记:捷文规范

1.本文成长记录及勘误表
项目源码 日期 备注
V0.1-github 2018-1-11 Android多媒体之GL-ES战记第一集--勇者集结
2.更多关于我
笔名 QQ 微信 爱好
张风捷特烈 1981462002 zdl1994328 语言
我的github 我的简书 我的掘金 个人网站

本文参考:
1.《Android 3D游戏开发技术宝典 OpenGL ES 2.0》
2.OpenGL ES 学习记录
3.opengl-tutorial:OpenGL基础知识
4.广大网友的文章零散参考,就不一一列举了

Android多媒体之GL-ES战记第一集--勇者集结相关推荐

  1. Android多媒体之GL-ES战记第二集--谜团立方

    上集回顾 旁白:上集说到,为了获取黑龙宝藏,勇者集结,共闯黑龙洞穴 经过一路艰辛,终于过了第四副本,前面还有什么困难等待着他们?一起收看 第五副本:龙之图阵 1.第一关卡:画一个矩形 NPC:隐藏任务 ...

  2. Android多媒体之GLES2战记第四集--移形换影

    视野限制了人对这个宇宙的认知,但没有视野,人将会一无所知 上集说到勇者坠入黑暗之渊,凭借对世界的认知构建出了世界系 到此为止,OpenGL的世界观已经映入脑海,新手十二副本已经通过 接下来等待他们的将 ...

  3. 【深入理解 android telephony 系列第一集 初识telephony】

    深入理解 android telephony 深入理解 android telephony 系列第一集 初识telephony 1. 概述 2. Android Telephony 框架 3. And ...

  4. 在Android中使用OpenGL ES开发第(五)节:GLSL基础语法

    一.前期基础储备 笔者之前的四篇文综述了Android中使用OpenGL ES绘制基本图形和实现了简单的相机预览,初次接触OpenGL ES开发的读者可能对其中新的概念比较迷惑,尤其是其中的顶点着色器 ...

  5. android平台下OpenGL ES 3.0绘制圆点、直线和三角形

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  6. Android 多媒体之音频----(日积月累)

    在开发上,习惯的将音频.视频功能的使用称之为多媒体,实际上如果讲的宽泛一些的话,相机的使用,比如拍照,录制视频等,也可以划分到多媒体的范畴里面. 从本节课开始,我们就来看看Android中多媒体的AP ...

  7. 《Android多媒体应用开发实战详解:图像、音频、视频、2D和3D》——2.1节简析Android安装文件...

    本节书摘来自异步社区<Android多媒体应用开发实战详解:图像.音频.视频.2D和3D>一书中的第2章,第2.1节简析Android安装文件,作者 王石磊 , 吴峥,更多章节内容可以访问 ...

  8. Android 多媒体之音频

    在开发上,习惯的将音频.视频功能的使用称之为多媒体,实际上如果讲的宽泛一些的话,相机的使用,比如拍照,录制视频等,也可以划分到多媒体的范畴里面. 从本节课开始,我们就来看看Android中多媒体的AP ...

  9. Android多媒体开发

    Android多媒体开发系列文章 Android多媒体开发:录音机 Android多媒体开发:照相机 TextureView+MediaPlayer实现在线短视频播放 Android多媒体开发:第三方 ...

  10. Android 多媒体视频播放一( 多媒体理解与经验分享)

    前言 说到android的多媒体,一把辛酸一把泪,当初听说会多媒体的比较牛掰,公司也有需求,于是乎我也积极的加入研究android多媒体的行列,记得以前刚接触的时候,最开始还是比较头大的,主要是但是很 ...

最新文章

  1. 面部识别必看!5篇顶级论文了解如何实现人脸反欺诈、跨姿势识别等(附链接)...
  2. MySQL_项目7: 各部门工资最高的员工(难度:中等)
  3. .net Core+Dapper MySQL增删改查
  4. Insufficient free space for journal files
  5. 用twisted为未来安排任务(Scheduling tasks for the future
  6. SAP BC417 课程中文自学笔记
  7. Spring原理只要看这篇文章就够了
  8. ssis sql_SSIS OLE DB来源:SQL命令与表或视图
  9. 每天学一点flash(6) FLASH 8 和 FLASH CS3 加载外部文本的区别 (转载)
  10. win7右键我的电脑管理菜单失效的解决办法
  11. Python动作冒险类游戏推荐:一款奔跑的玛丽冒险岛游戏、你能坚持几天丫?
  12. 小程序毕设作品之微信美食菜谱小程序毕业设计成品(6)开题答辩PPT
  13. 前端移动端开发(基础)
  14. 专利写作规范及如何写一篇专利
  15. Data URL和图片
  16. PDF文件拆分合并器PDF Merge PDF Splitter + Mac
  17. boost之日期 时间(date_time)
  18. 图片,文字在线转为字符画
  19. 狼 我一头独狼 在狂奔 嗅找正正狼群
  20. 红牛农场java代码_Java面向对象程序设计实验指导模板代码(171页)-原创力文档...

热门文章

  1. 【中间件系列】Nacos注册中心妙用
  2. Surface Pro 4 系统优化全教程及QA
  3. 笔记本外接显示器教程级后续使用技巧
  4. 【数据集划分】误用shuffle,导致训练集和测试集掺混
  5. Katalon Recorder安装及使用
  6. 【数据结构与算法】动态规划
  7. vue中用cdn引入优化vender.js大小,和cdn 引入mint-ui的问题
  8. 如何准备机器学习工程师的面试 ?
  9. 国内的邮箱哪最好用,个人的邮箱排名?
  10. matlab蚁群算法解决vrp过程,蚁群算法MATLAB解VRP问题