Android Camera&Matrix图像变换

Camera与Matrix

Android UI系统中,Camera充当着相机的角色,无论是系统成像还是UI绘制。都离不开Camera。但是在Android系统中,存在两种Camera,一种是视觉成像的(拍照、摄像),另一种是图形绘制(游戏、地图、3D),实际上两种也都离不开Matrix,所以本质上可以理解为,一个负责对相机以外的物体成像,一个负责Android View的成像。这里我们重点来介绍系统UI成像的Camera。

        UI成像需要使用到画布和坐标系。在Android系统中,View最终以二维方式显示,但是Camera是三维成像,遇到这种问题,我们这里需要用到Matrix了,它用来将三维转为二维。原理是Matrix矩阵将Camera的投影转到Canvas上,因此我们就能看见3D图形显示在二维坐标系中了。

关于Matrix请参阅:

android matrix 最全方法详解与进阶(完整篇)

Android中利用Camera与Matrix实现3D效果详解

Camera坐标系与Android坐标系

  camera的坐标系是左手坐标系。伸出左手,让拇指和食指成L形,大拇指向右,食指向上,中指指向前方,这样我们就建立了一个左手坐标系,拇指,食指,中指的指向分别代表了x,y,z轴的正方向。如下图所示:

下面是一些细节点:

1,camera位于坐标点(0,0),也就是视图的左上角;

2,camera.translate(10, 20, 30)的意思是把观察物体右移10,上移20,向前移30(即让物体远离camera,这样物体将会变小);
3,camera.rotateX(45)的意思是绕x轴顺时针旋转45度。举例来说,如果物体中间线和x轴重合的话,绕x轴顺时针旋转45度就是指物体上半部分向里翻转,下半部分向外翻转;
4,camera.rotateY(45)的意思是绕y轴顺时针旋转45度。举例来说,如果物体中间线和y轴重合的话,绕y轴顺时针旋转45度就是指物体右半部分向里翻转,左半部分向外翻转;
5,camera.rotateZ(45)的意思是绕z轴顺时针旋转45度。举例来说,如果物体中间线和z轴重合的话,绕z轴顺时针旋转45度就是指物体上半部分向左翻转,下半部分向右翻转;

Android坐标系是二维的

1,camera位于坐标点(0,0),也就是视图的左上角;

2,垂直向下为y轴正方向

3、垂直向右为x轴正方向

两类坐标系比较

Camera与Matrix API

Camera创建一个没有任何转换效果的新的Camera实例

  • applyToCanvas(Canvas canvas) 根据当前的变换计算出相应的矩阵,然后应用到制定的画布上
  • getLocationX() 获取Camera的x坐标
  • getLocationY() 获取Camera的y坐标
  • getLocationZ() 获取Camera的z坐标
  • getMatrix(Matrixmatrix) 获取转换效果后的Matrix对象
  • restore() 恢复保存的状态
  • rotate(float x, float y, float z) 沿X、Y、Z坐标进行旋转
  • rotateX(float deg)
  • rotateY(float deg)
  • rotateZ(float deg)
  • save() 保存状态
  • setLocation(float x, float y, float z)
  • translate(float x, float y, float z)沿X、Y、Z轴进行平移

Matrix相关方法如下

  • setTranslate(floatdx,floatdy):控制Matrix进行平移
  • setSkew(floatkx,floatky,floatpx,floatpy):控制Matrix以px,py为轴心进行倾斜,kx,ky为X,Y方向上的倾斜距离
  • setRotate(floatdegress):控制Matrix进行旋转,degress控制旋转的角度
  • setRorate(floatdegress,floatpx,floatpy):设置以px,py为轴心进行旋转,degress控制旋转角度
  • setScale(floatsx,floatsy):设置Matrix进行缩放,sx,sy控制X,Y方向上的缩放比例
  • setScale(floatsx,floatsy,floatpx,floatpy):设置Matrix以px,py为轴心进行缩放,sx,sy控制X,Y方向上的缩放比例

API提供了set、post和pre三种操作,下面这个重点看下,之后效果会用到

post是后乘,当前的矩阵乘以参数给出的矩阵。可以连续多次使用post,来完成所需的整个变换。

pre是前乘,参数给出的矩阵乘以当前的矩阵。所以操作是在当前矩阵的最前面发生的。

导演与摄像机

在3D摄影中,导演控制镜头位置来呈现不同的效果,摄像机在空间的不同位置展现出来的效果是不同的。由于我们无法直接用眼睛去观察这一个空间,所以要借助摄像机采集信息,制成2D影像供我们观察。简单来说,摄像机就是我们观察虚拟3D空间的眼睛,而我们既是导演又是观众。我们在电视上看到的都是三维投影。

注意:摄像机的位置默认是 (0, 0, -576)

三维投影

三维投影是将三维空间中的点映射到二维平面上的方法。由于目前绝大多数图形数据的显示方式仍是二维的,因此三维投影的应用相当广泛,尤其是在计算机图形学,工程学和工程制图中。

三维投影一般有两种,正交投影 和 透视投影

正交投影就是我们数学上学过的 “正视图、正视图、侧视图、俯视图” 这些东西。

透视投影则更像拍照片,符合近大远小的关系,有立体感,我们此处使用的就是透视投影。

实战

简单示例

原始图

转换图

第二张图实际上是摄像机分别向x,y,z移动了(这种效果的转变,我们可以假定在View原图已经绘制完成的情况下,拿一个相机去拍摄,然后再次将投影通过Materix转到Canvas上)

代码如下:

1public class CameraTestView extends View{ 2 3 private Camera camera; 4 private Matrix matrix; 5 private Paint paint; 6 public CameraTestView(Context context, AttributeSet attrs) { 7 super(context, attrs); 8 camera = new Camera(); 9 matrix = new Matrix(); 10 setBackgroundColor(Color.parseColor("#3f51b5")); 11 paint = new Paint(Paint.ANTI_ALIAS_FLAG); 12 paint.setStyle(Style.FILL); 13 paint.setColor(Color.parseColor("#ff4081")); 14 } 15 16 @Override 17 protected void onDraw(Canvas canvas) { 18 matrix.reset(); 19 camera.save(); 20 camera.translate(10, 50, -180); 21 camera.getMatrix(matrix); 22 camera.restore(); 23 canvas.concat(matrix); 24 25 canvas.drawCircle(60, 60, 60, paint); 26 } 27 28}

3D旋转动画示例

1public class Rotate3dAnimation extends Animation { 2 private final float mFromDegrees; 3 private final float mToDegrees; 4 private final float mCenterX; 5 private final float mCenterY; 6 private final float mDepthZ; 7 private final boolean mReverse; 8 private Camera mCamera; 9 float scale = 1; // 10 11 /** 12 * 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。 13 * @param context 14 public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees, 15 float centerX, float centerY, float depthZ, boolean reverse) { 16 mFromDegrees = fromDegrees; 17 mToDegrees = toDegrees; 18 mCenterX = centerX; 19 mCenterY = centerY; 20 mDepthZ = depthZ; 21 mReverse = reverse; 22 23 // 获取手机像素密度 (即dp与px的比例) 24 scale = context.getResources().getDisplayMetrics().density; 25 } 26 27 @Override 28 public void initialize(int width, int height, int parentWidth, int parentHeight) { 29 super.initialize(width, height, parentWidth, parentHeight); 30 mCamera = new Camera(); 31 } 32 33 @Override 34 protected void applyTransformation(float interpolatedTime, Transformation t) { 35 final float fromDegrees = mFromDegrees; 36 float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); 37 final float centerX = mCenterX; 38 final float centerY = mCenterY; 39 final Camera camera = mCamera; 40 final Matrix matrix = t.getMatrix(); 41 camera.save(); 42 43 // 调节深度 44 if (mReverse) { 45 camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime); 46 } else { 47 camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime)); 48 } 49 50 // 绕y轴旋转 51 camera.rotateY(degrees); 52 53 camera.getMatrix(matrix); 54 camera.restore(); 55 56 // 修正失真,主要修改 MPERSP_0 和 MPERSP_1 57 float[] mValues = new float[9]; 58 matrix.getValues(mValues); //获取数值 59 mValues[6] = mValues[6]/scale; //数值修正 60 mValues[7] = mValues[7]/scale; //数值修正 61 matrix.setValues(mValues); //重新赋值 62 63 // 调节中心点 64 matrix.preTranslate(-centerX, -centerY); 65 matrix.postTranslate(centerX, centerY); 66 } 67}

当然,以上的实现方式较复杂,我们可以使用Animation或者Animator来实现,通过rotationY动画。

1 final float targetVal = 180f; 2 final int width = v.getMeasuredWidth(); 3 v.setPivotX(width/2); 4 v.clearAnimation(); 5 final Drawable before= getResources().getDrawable(R.mipmap.img_cake); 6 final Drawable after = getResources().getDrawable(R.mipmap.img_heart); 7 Animator animator = ObjectAnimator.ofFloat(v,"rotationY",targetVal,360); 8 animator.setDuration(1000); 9 v.setRotationY(180f); 10 v.setBackground(before); 11 final float distance = v.getCameraDistance(); //获取相机距离 12 13 ((ValueAnimator)animator).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 14 @Override 15 public void onAnimationUpdate(ValueAnimator animation) { 16 float value = (float) animation.getAnimatedValue(); 17 float fraction = animation.getAnimatedFraction(); 18 19 if(Math.abs(360+180)/2<=Math.abs(value)){ 20 v.setBackground(after); 21 } 22 float f = (float) Math.abs(Math.sin(Math.toRadians(fraction * targetVal))); 23 Log.i("Animator","value="+value +" ,fraction="+fraction+", f="+f); 24 v.setCameraDistance(distance + f*(distance*width)/2); 25 } 26 }); 27 28 animator.start(); 29 }

摄像机要求 由近到远,然后由远到近,这里我们通过三角函数sin来实现

float f = (float) Math.abs(Math.sin(Math.toRadians(fraction * targetVal)));
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )