OpenglES2.0 for Android:各种变换来一波
监听屏幕事件
在进行各种变换之前,我们先来了解一下如何监听屏幕的事件。我们下面的变换都需要用立方体来演示,所以我们继续使用上一节的绘制立方体的内容
首先新建一个项目 OpengESChange ,将上一节中关于绘制立方体的代码复制过来 。在前面我们一直在使用
在第一篇中我们已经知道了这个类的作用,为了监听屏幕事件,我们创建一个类继承自该类,重写其onTouchEvent方法。
android.opengl.GLSurfaceView
在第一篇中我们已经知道了这个类的作用,为了监听屏幕事件,我们创建一个类继承自该类,重写其onTouchEvent方法。
此时该类 代码如下 : (MySurfaceView.java ):
package com.cumt.opengeschange;
import com.cumt.render.MyRender;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
public class MySurfaceView extends GLSurfaceView {
private MyRender myRender;
public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
myRender = new MyRender(context);
this.setEGLContextClientVersion(2);
this.setRenderer(myRender);
// 设置渲染模式为主动渲染
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return super.onTouchEvent(event);
}
}
在onTouchEvent方法中我们就可以检测到各种事件了 我们也可以使用GLSurfaceView的setOnTouchListener来监听视图的触控事件 ,代码如下(MySurfaceView.java ):
package com.cumt.opengeschange;
import com.cumt.render.MyRender;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
import android.view.View;
public class MySurfaceView extends GLSurfaceView {
private MyRender myRender;
public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
myRender = new MyRender(context);
this.setEGLContextClientVersion(2);
this.setRenderer(myRender);
// 设置渲染模式为主动渲染
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
this.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}
});
}
}
package com.cumt.opengeschange;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
private MySurfaceView glSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置为全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 设置为横屏模式
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
glSurfaceView = new MySurfaceView(this);
setContentView(glSurfaceView);
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
glSurfaceView.onPause();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
glSurfaceView.onResume();
}
}
此时我们的前期工作已经OK 了
平移变换
先来看下平移变换矩阵 :
矩阵中的三个参数分别表示沿X ,Y,Z轴方向的位移
平移矩阵M 乘 当前P点的向量,即可得到平移后的P ‘ 点的向量 。
现在我们想要在原来绘制立方体的基础上实现这样的功能 :手指在屏幕上向左或向右移动时,我们的立方体向左或向右移动一段距离 。
首先我们需要修改MatrixState中的代码 ,新建一个平移变换矩阵 并初始化为单位矩阵,然后创建一个方法共外部调用以设置平移,最后修改我们原先的
getFinalMatrix方法,在该方法中乘上该平移矩阵。过程如下代码所示 (MatrixState.java ):
package com.cumt.utils;
import android.opengl.Matrix;
//存储系统矩阵状态的类
public class MatrixState {
private static float[] mProjMatrix = new float[16];// 4x4矩阵 存储投影矩阵
private static float[] mVMatrix = new float[16];// 摄像机位置朝向9参数矩阵
/*
* 第一步 :新建平移变换矩阵
*/
private static float[] mtMatrix = new float[16];// 平移变换矩阵
/*
* 第二步: 初始化为单位矩阵
*/
static{
//初始化为单位矩阵
Matrix.setIdentityM(mtMatrix, 0);
}
/*
* 第三步 : 平移变换方法共外部使用
*/
public static void translate(float x,float y,float z)//设置沿xyz轴移动
{
Matrix.translateM(mtMatrix, 0, x, y, z);
}
// 设置摄像机
public static void setCamera(float cx, // 摄像机位置x
float cy, // 摄像机位置y
float cz, // 摄像机位置z
float tx, // 摄像机目标点x
float ty, // 摄像机目标点y
float tz, // 摄像机目标点z
float upx, // 摄像机UP向量X分量
float upy, // 摄像机UP向量Y分量
float upz // 摄像机UP向量Z分量
) {
Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
}
// 设置透视投影参数
public static void setProjectFrustum(float left, // near面的left
float right, // near面的right
float bottom, // near面的bottom
float top, // near面的top
float near, // near面距离
float far // far面距离
) {
Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
}
// 获取具体物体的总变换矩阵
static float[] mMVPMatrix = new float[16];
public static float[] getFinalMatrix() {
/*
* 第四步 : 乘以平移变换矩阵
*/
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mtMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
}
这里我们使用静态代码段初始化平移矩阵为单位矩阵,如果用户没有设置平移变换矩阵,也不会影响原图形。
然后我们在MySurfaceView中监听用户的屏幕移动事件 ,代码如下 (MySurfaceView.java):
package com.cumt.opengeschange;
import com.cumt.render.MyRender;
import com.cumt.utils.MatrixState;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
import android.view.View;
public class MySurfaceView extends GLSurfaceView {
private MyRender myRender;
private float mPreviousX;//上次的触控位置X坐标
public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
myRender = new MyRender(context);
this.setEGLContextClientVersion(2);
this.setRenderer(myRender);
// 设置渲染模式为主动渲染
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
this.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
float x = event.getX();//当前的触控位置X坐标
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE://检测到移动事件时
float dx = x - mPreviousX;
if(dx > 0){
MatrixState.translate(0.1f, 0, 0);
}else{
MatrixState.translate(-0.1f, 0, 0);
}
}
mPreviousX=x;
return true;
}
});
}
}
来看下运行效果 :
旋转变换
看下旋转矩阵 :
表示将点 P 绕向量 U 旋转
θ 度 。在OpenGL中我们使用
void android.opengl.
Matrix.rotateM(
float[] m,
int mOffset,
float a,
float x,
float y,
float z)
方法来设置旋转 ,第一个参数表示返回的旋转矩阵,mOffset表示偏移量,一般设置为0 ,a 表示角度 ,x y z表示旋转轴对应向量的X,Y,Z分量
下面我们来实现这样一个功能 : 每点击屏幕一次,让我们的立方体沿着其 Y 轴旋转30度
首先在我们原来的 MatrixState类中新增一个方法来设置旋转 ,新增代码如下 :
//旋转变换
public static void rotate(float angle, float x, float y, float z) {// 设置绕xyz轴移动
Matrix.rotateM(mtMatrix, 0, angle, x, y, z);
}
然后就在MySurfaceView类中设置监听事件和旋转 ,此时MySurfaceView.java代码如下 :
package com.cumt.opengeschange;
import com.cumt.render.MyRender;
import com.cumt.utils.MatrixState;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
import android.view.View;
public class MySurfaceView extends GLSurfaceView {
private MyRender myRender;
public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
myRender = new MyRender(context);
this.setEGLContextClientVersion(2);
this.setRenderer(myRender);
// 设置渲染模式为主动渲染
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
this.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN://检测到点击事件时
MatrixState.rotate(30, 0, 1, 0);
}
return true;
}
});
}
}
我们运行看下效果:
缩放变换
缩放矩阵 :
上面矩阵中三个参数分别表示缩放变换中的沿X,Y,Z轴方向的缩放率。
我们来实现下面的效果:点击屏幕后进行一定比例的缩放操作
首先在MatrixState.java中加入如下代码 :
//缩放变换
public static void scale(float x,float y,float z)
{
Matrix.scaleM(mtMatrix,0, x, y, z);
}
package com.cumt.opengeschange;
import com.cumt.render.MyRender;
import com.cumt.utils.MatrixState;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
import android.view.View;
public class MySurfaceView extends GLSurfaceView {
private MyRender myRender;
public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
myRender = new MyRender(context);
this.setEGLContextClientVersion(2);
this.setRenderer(myRender);
// 设置渲染模式为主动渲染
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
this.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN://检测到点击事件时
MatrixState.scale(0.4f, 1.5f, 0.6f);//xyz三个方向按各自的缩放因子进行缩放
}
return true;
}
});
}
}
运行效果:
最后祝大家端午节快乐~~