OpenglES2.0 for Android:各种变换来一波

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cassiePython/article/details/51606205

OpenglES2.0 for Android:各种变换来一波

监听屏幕事件

在进行各种变换之前,我们先来了解一下如何监听屏幕的事件。我们下面的变换都需要用立方体来演示,所以我们继续使用上一节的绘制立方体的内容
首先新建一个项目 OpengESChange ,将上一节中关于绘制立方体的代码复制过来 。在前面我们一直在使用
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;
			}
		});
	}
}


此时MainActivity也有稍微的改动 (MainActivity.java ):

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);
    }

然后就在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.scale(0.4f, 1.5f, 0.6f);//xyz三个方向按各自的缩放因子进行缩放
				}
				return true;
			}
		});
	}
}


运行效果:



最后祝大家端午节快乐~~


展开阅读全文

没有更多推荐了,返回首页