android 坐标转换dp,Android Camera&Matrix图像变换

本文介绍了Android系统中Camera在UI成像中的作用,讲解了如何利用Matrix将三维物体转化为二维显示。内容涵盖Camera坐标系、Matrix变换以及3D旋转动画的实现,强调了Camera和Matrix在图形绘制中的重要性,同时通过代码示例展示了如何进行3D旋转动画的编程。
摘要由CSDN通过智能技术生成

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请参阅:

Camera坐标系与Android坐标系

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

da47de0220ac2d3a9698dc06bc6ad5d9.png

下面是一些细节点:

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轴正方向

两类坐标系比较

6975ca14d41a39a64e9e2101eeef4d1f.png

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空间的眼睛,而我们既是导演又是观众。我们在电视上看到的都是三维投影。

fb2e4fc4364567ecb6715ee95ccaef07.gif

49af3613d76e53b6a9e86c8361a10c12.gif

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

三维投影

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

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

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

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

实战

简单示例

原始图

转换图

34b9578a565c18580a65a8dd0d267410.png

776258d831c5416a7a92a0ec9d134fb6.png

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

代码如下:

public class CameraTestView extends View{

private Camera camera;

private Matrix matrix;

private Paint paint;

public CameraTestView(Context context, AttributeSet attrs) {

super(context, attrs);

camera = new Camera();

matrix = new Matrix();

setBackgroundColor(Color.parseColor("#3f51b5"));

paint = new Paint(Paint.ANTI_ALIAS_FLAG);

paint.setStyle(Style.FILL);

paint.setColor(Color.parseColor("#ff4081"));

}

@Override

protected void onDraw(Canvas canvas) {

matrix.reset();

camera.save();

camera.translate(10, 50, -180);

camera.getMatrix(matrix);

camera.restore();

canvas.concat(matrix);

canvas.drawCircle(60, 60, 60, paint);

}

}

3D旋转动画示例

f6f536630529c550e93ef170a182026d.gif

public class Rotate3dAnimation extends Animation {

private final float mFromDegrees;

private final float mToDegrees;

private final float mCenterX;

private final float mCenterY;

private final float mDepthZ;

private final boolean mReverse;

private Camera mCamera;

float scale = 1; //

/**

* 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。

* @param context

public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees,

float centerX, float centerY, float depthZ, boolean reverse) {

mFromDegrees = fromDegrees;

mToDegrees = toDegrees;

mCenterX = centerX;

mCenterY = centerY;

mDepthZ = depthZ;

mReverse = reverse;

// 获取手机像素密度 (即dp与px的比例)

scale = context.getResources().getDisplayMetrics().density;

}

@Override

public void initialize(int width, int height, int parentWidth, int parentHeight) {

super.initialize(width, height, parentWidth, parentHeight);

mCamera = new Camera();

}

@Override

protected void applyTransformation(float interpolatedTime, Transformation t) {

final float fromDegrees = mFromDegrees;

float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);

final float centerX = mCenterX;

final float centerY = mCenterY;

final Camera camera = mCamera;

final Matrix matrix = t.getMatrix();

camera.save();

// 调节深度

if (mReverse) {

camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);

} else {

camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));

}

// 绕y轴旋转

camera.rotateY(degrees);

camera.getMatrix(matrix);

camera.restore();

// 修正失真,主要修改 MPERSP_0 和 MPERSP_1

float[] mValues = new float[9];

matrix.getValues(mValues); //获取数值

mValues[6] = mValues[6]/scale;//数值修正

mValues[7] = mValues[7]/scale;//数值修正

matrix.setValues(mValues); //重新赋值

// 调节中心点

matrix.preTranslate(-centerX, -centerY);

matrix.postTranslate(centerX, centerY);

}

}

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

final float targetVal = 180f;

final int width = v.getMeasuredWidth();

v.setPivotX(width/2);

v.clearAnimation();

final Drawable before= getResources().getDrawable(R.mipmap.img_cake);

final Drawable after = getResources().getDrawable(R.mipmap.img_heart);

Animator animator = ObjectAnimator.ofFloat(v,"rotationY",targetVal,360);

animator.setDuration(1000);

v.setRotationY(180f);

v.setBackground(before);

final float distance = v.getCameraDistance(); //获取相机距离

((ValueAnimator)animator).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

float value = (float) animation.getAnimatedValue();

float fraction = animation.getAnimatedFraction();

if(Math.abs(360+180)/2<=Math.abs(value)){

v.setBackground(after);

}

float f = (float) Math.abs(Math.sin(Math.toRadians(fraction * targetVal)));

Log.i("Animator","value="+value +" ,fraction="+fraction+", f="+f);

v.setCameraDistance(distance + f*(distance*width)/2);

}

});

animator.start();

}

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

float f = (float) Math.abs(Math.sin(Math.toRadians(fraction * targetVal)));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值