Android多媒体之GL-ES战记第一集--勇者集结
前言
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.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);// 将坐标添加到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.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: 尊敬的勇者-张风捷特烈,祝您一路平安,成功斩杀黑龙...
第一集结束,下一集:"谜团立方" 敬请期待
后记:捷文规范
1.本文成长记录及勘误表
项目源码 | 日期 | 备注 |
---|---|---|
V0.1-github | 2018-1-11 | Android多媒体之GL-ES战记第一集--勇者集结 |
2.更多关于我
笔名 | 微信 | 爱好 | |
---|---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 | 语言 |
我的github | 我的简书 | 我的掘金 | 个人网站 |
本文参考:
1.《Android 3D游戏开发技术宝典 OpenGL ES 2.0》
2.OpenGL ES 学习记录
3.opengl-tutorial:OpenGL基础知识
4.广大网友的文章零散参考,就不一一列举了
Android多媒体之GL-ES战记第一集--勇者集结相关推荐
- Android多媒体之GL-ES战记第二集--谜团立方
上集回顾 旁白:上集说到,为了获取黑龙宝藏,勇者集结,共闯黑龙洞穴 经过一路艰辛,终于过了第四副本,前面还有什么困难等待着他们?一起收看 第五副本:龙之图阵 1.第一关卡:画一个矩形 NPC:隐藏任务 ...
- Android多媒体之GLES2战记第四集--移形换影
视野限制了人对这个宇宙的认知,但没有视野,人将会一无所知 上集说到勇者坠入黑暗之渊,凭借对世界的认知构建出了世界系 到此为止,OpenGL的世界观已经映入脑海,新手十二副本已经通过 接下来等待他们的将 ...
- 【深入理解 android telephony 系列第一集 初识telephony】
深入理解 android telephony 深入理解 android telephony 系列第一集 初识telephony 1. 概述 2. Android Telephony 框架 3. And ...
- 在Android中使用OpenGL ES开发第(五)节:GLSL基础语法
一.前期基础储备 笔者之前的四篇文综述了Android中使用OpenGL ES绘制基本图形和实现了简单的相机预览,初次接触OpenGL ES开发的读者可能对其中新的概念比较迷惑,尤其是其中的顶点着色器 ...
- android平台下OpenGL ES 3.0绘制圆点、直线和三角形
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- Android 多媒体之音频----(日积月累)
在开发上,习惯的将音频.视频功能的使用称之为多媒体,实际上如果讲的宽泛一些的话,相机的使用,比如拍照,录制视频等,也可以划分到多媒体的范畴里面. 从本节课开始,我们就来看看Android中多媒体的AP ...
- 《Android多媒体应用开发实战详解:图像、音频、视频、2D和3D》——2.1节简析Android安装文件...
本节书摘来自异步社区<Android多媒体应用开发实战详解:图像.音频.视频.2D和3D>一书中的第2章,第2.1节简析Android安装文件,作者 王石磊 , 吴峥,更多章节内容可以访问 ...
- Android 多媒体之音频
在开发上,习惯的将音频.视频功能的使用称之为多媒体,实际上如果讲的宽泛一些的话,相机的使用,比如拍照,录制视频等,也可以划分到多媒体的范畴里面. 从本节课开始,我们就来看看Android中多媒体的AP ...
- Android多媒体开发
Android多媒体开发系列文章 Android多媒体开发:录音机 Android多媒体开发:照相机 TextureView+MediaPlayer实现在线短视频播放 Android多媒体开发:第三方 ...
- Android 多媒体视频播放一( 多媒体理解与经验分享)
前言 说到android的多媒体,一把辛酸一把泪,当初听说会多媒体的比较牛掰,公司也有需求,于是乎我也积极的加入研究android多媒体的行列,记得以前刚接触的时候,最开始还是比较头大的,主要是但是很 ...
最新文章
- 面部识别必看!5篇顶级论文了解如何实现人脸反欺诈、跨姿势识别等(附链接)...
- MySQL_项目7: 各部门工资最高的员工(难度:中等)
- .net Core+Dapper MySQL增删改查
- Insufficient free space for journal files
- 用twisted为未来安排任务(Scheduling tasks for the future
- SAP BC417 课程中文自学笔记
- Spring原理只要看这篇文章就够了
- ssis sql_SSIS OLE DB来源:SQL命令与表或视图
- 每天学一点flash(6) FLASH 8 和 FLASH CS3 加载外部文本的区别 (转载)
- win7右键我的电脑管理菜单失效的解决办法
- Python动作冒险类游戏推荐:一款奔跑的玛丽冒险岛游戏、你能坚持几天丫?
- 小程序毕设作品之微信美食菜谱小程序毕业设计成品(6)开题答辩PPT
- 前端移动端开发(基础)
- 专利写作规范及如何写一篇专利
- Data URL和图片
- PDF文件拆分合并器PDF Merge PDF Splitter + Mac
- boost之日期 时间(date_time)
- 图片,文字在线转为字符画
- 狼 我一头独狼 在狂奔 嗅找正正狼群
- 红牛农场java代码_Java面向对象程序设计实验指导模板代码(171页)-原创力文档...