Android Camera&Matrix圖像變換

Camera與Matrix

Android UI系統中,Camera充當着相機的角色,不管是系統成像仍是UI繪製。都離不開Camera。可是在Android系統中,存在兩種Camera,一種是視覺成像的(拍照、攝像),另外一種是圖形繪製(遊戲、地圖、3D),實際上兩種也都離不開Matrix,因此本質上能夠理解爲,一個負責對相機之外的物體成像,一個負責Android View的成像。這裏咱們重點來介紹系統UI成像的Camera。android

        UI成像須要使用到畫布和座標系。在Android系統中,View最終以二維方式顯示,可是Camera是三維成像,遇到這種問題,咱們這裏須要用到Matrix了,它用來將三維轉爲二維。原理是Matrix矩陣將Camera的投影轉到Canvas上,所以咱們就能看見3D圖形顯示在二維座標系中了。canvas

關於Matrix請參閱:app

android matrix 最全方法詳解與進階(完整篇)ide

Android中利用Camera與Matrix實現3D效果詳解函數

 

 

Camera座標系與Android座標系

  camera的座標系是左手座標系。伸出左手,讓拇指和食指成L形,大拇指向右,食指向上,中指指向前方,這樣咱們就創建了一個左手座標系,拇指,食指,中指的指向分別表明了x,y,z軸的正方向。以下圖所示:post

下面是一些細節點:動畫

1,camera位於座標點(0,0),也就是視圖的左上角;spa

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度就是指物體上半部分向左翻轉,下半部分向右翻轉;.net

 

Android座標系是二維的3d

1,camera位於座標點(0,0),也就是視圖的左上角;

2,垂直向下爲y軸正方向

三、垂直向右爲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上)

代碼以下:

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旋轉動畫示例

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)));
相關文章
相關標籤/搜索