一、概述

在Android中说到3D开发,我们首先想到的是OpenGL,但用起来比较复杂繁琐,不适合做应用级别的3D变换。Android为我们提供了一个简化版的3D开发入口:Camera(这里的Camera位于android.graphics下,需要区分android.hardware下用于拍照的Camera类):

(1)Camera工作原理

我们看一下android.graphics下的Camera介绍:

/**
* A camera instance can be used to compute 3D transformations and
* generate a matrix that can be applied, for instance, on a
* {@link Canvas}.
*/

Camera可以做3D变换并且导出一个matrix供其他对象(如Canvas)使用。

官方解释非常精准地表达了Camera的作用,我再来扩展一下:Camera并不直接操纵View或绘制类来实现变换效果,而是通过自身3D变换后导出结果到一个matrix,然后将这个matrix与其他实例的的matrix对接!

比如Canvas的matrix

  canvas.setMatrix(matrix);
  canvas.concat(matrix);

或者Animation 中Transformation的matrix

    @Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
}

举个简单的例子:

代码如下:

    @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
camera.save(); //保存Camera
camera.rotateY(45); //绕Y轴旋转45度
camera.getMatrix(matrix); //Camera将变换输出到Matrix
camera.restore(); //恢复Camera
matrix.preTranslate(-dog.getWidth() / 2, -dog.getHeight() / 2); //旋转之前将图片中心点与坐标系原点对齐
matrix.postTranslate(dog.getWidth() / 2, dog.getHeight() / 2); //旋转之后将图片移动回原位置
matrix.postTranslate(50, 70); //因旋转后图片左上角超出了View边界,将其向右下角移动一点,以便显示完整
canvas.concat(matrix); //将Camera导出的Matrix和Canvas对接
canvas.drawBitmap(dog, 0, 0, paint); //绘制图片
}

那么,我们来总结一下Camera的工作原理:Android中所有几何变换的底层数学模型都是matrix矩阵变换,Camera有很好的3D变换的方法,且变换结果可以输出到指定的matrix上,我们继而将其映射到Canvas或Transformation等的matrix上,使其达到相同的3D变换效果。这里Camera实际上扮演了3D变换方案输出者的角色。

(2)两个坐标系

到目前为止,你肯定会有一个疑问:既然Camera和Canvas的几何变换对应的都是matrix,那Canvas为什么不自己提供一套3D接口,而要去借用Camera呢?这要牵扯出更底层的设计。

首先,Canvas是做View绘制的,其对应的坐标系自然是View的二维平面坐标系,在Android中其设计如下:

让Canvas基于平面坐标系输出三维空间变换矩阵,实在是强人所难!

Camera就不一样了,其底层依赖OpenGL,玩的就是空间坐标系,其设计符合左手坐标系(这点网上有人怀疑,本人验证确认轴向符合左手坐标系,但是Y轴旋转方向是反的):

将手机竖直正面放置,向右是X轴正向,向上是Y轴正向,垂直屏幕向里是Z轴正向。

两个坐标系的区别:

坐标系 2D 3D
原点位置 左上角
X轴正向
Y轴正向
Z轴正向 垂直屏幕向里

所以Camera是基于空间坐标系,可以提供很好的3D变换方法;这是Canvas们无法做到的。

既然说到了坐标系,我们不妨继续往下刨:既然Camera和Canvas几何变换的底层模型都是矩阵变换,那我能不能绕过Canvas,直接去操作它的Matrix,让它像Camera那样做3D变换?对不起,就算你的大学几何学得很好,答案依然是不可能(尽管我知道这很容易被打脸,有人可能会说通过Matrix的setPolyToPoly方法可以实现任意变换,我只能说它依然只是2D模仿3D,不是真正意义上的3D空间变换)。为什么呢?这要深入研究一下Matrix了:

实际上,在Android中也有两个Matrix类:

  • 一个位于android.graphics包下,是一个3*3的矩阵,只能表示二维平面的几何变换,Canvas、Transformation和Camera输出的Matrix都是这个类
  • 另一个位移android.opengl包下,是一个4*4的矩阵,可以表示三维空间的几何变换,Camera的底层运算就是基于这个Matrix类

你可能也发现了,Camera做空间变换所用到的Matrix和最终输出到的Matrix不是同一个Matrix!为什么这样设计呢?我是这样理解的:

最初,View运行在二维坐标系,OpenGL存在于三维坐标系,两者互不相容;

后来,开发View的同学也希望做出像OpenGL里酷炫的三维效果,可是又不想因此引入复杂的OpenGL;

于是Google就基于OpenGL造了一个轻量级的3D开发工具——Camera,因为是方便View开发的同学用的,自然输出结果就是基于二维平面的Matrix了。

至于Camera将四阶矩阵映射成三阶矩阵的算法, 被封装在了Camera类getMatrix方法的native层,有兴趣可以去研究。


二、Camera实现3D动画

知道了Camera的三维变换原理,实现三维动画就不难了。连续的变换就是动画,连续的3D变换不就是3D动画嘛。

下面参考AppDemo的示例,效果如下:

自定义一个3D旋转的Animation,重写applyTransformation方法,对接Camera和Transformation的Matrix,核心代码:

    @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));
} // camera.rotateY(degrees); // 绕y轴旋转
camera.rotateX(degrees); // 绕x轴旋转
// camera.rotateZ(degrees); // 绕z轴旋转 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); //移回原位置
}

Github源码:https://github.com/JiaxtHome/AnimDemo


三、总结

现在我们已经知道简单的3D变换应该首先考虑Camera,下面总结一下Camera的关键API:

类别 相关方法 功能介绍
基本方法 save、store 保存、回滚
对接方法 getMatrix、applyToCanvas 获取Matrix、对接画布
变换方法 translate、rotate、rotate* 位移、旋转
相机位置 setLocation、getLocation* 设置、获取相机位置

Camera的方法并不多,其3D效果主要是通过旋转方法实现的。尽管如此,还是可以做出很多既实用又酷炫的动画切换效果,下面列举一些Github上的优秀例子:

Github源码地址:https://github.com/githubwing/ThreeDLayout

Github源码地址:https://github.com/ImmortalZ/StereoView

Github源码地址:https://github.com/youxiaochen/WheelView-3d

参考文献:

三维空间矩阵变换

二维图形的矩阵变换(一)——基本概念

安卓图形matrix矩阵变换的数学原理及代码

HenCoder Canvas对绘制的辅助和Matrix

GcsSloop 安卓自定义View进阶-Matrix Camera

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

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

Android Camera&Matrix图像变换

Camera三维动画的更多相关文章

  1. 在图层上使用CATransform3D制做三维动画-b

    在UIView上,我们可以使用CGAffineTransform来对视图进行:平移(translation),旋转(Rotation),缩 放(scale),倾斜(Invert)操作,但这些操作是没有 ...

  2. ArcGIS案例学习笔记4_2_城乡规划容积率计算和建筑景观三维动画

    ArcGIS案例学习笔记4_2_城乡规划容积率计算和建筑景观三维动画 概述 计划时间:第4天下午 目的:城市规划容积率计算和建筑三维景观动画 教程: pdf page578 数据:实验数据\Chp13 ...

  3. Autodesk Maya 2019 for Mac(三维动画软件)最新功能介绍

    Autodesk Maya是美国Autodesk公司出品的世界顶级的三维动画软件,应用对象是专业的影视广告,角色动画,电影特技等.Maya功能完善,工作灵活,易学易用,制作效率极高,渲染真实感极强,是 ...

  4. ArcGIS Engine三维动画开发 来自:http://www.iarcgis.com/?p=826

    ArcGIS Engine 三维开发 来自:http://www.iarcgis.com/?p=826 在三维中,经常使用的一个功能就是播放动画,也就是我们要对一条动画轨迹进行播放,而在ArcGIS ...

  5. 添加三维动画 demo

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...

  6. jquery和css3打造超梦幻的三维动画背景

    今天为大家带来的是一款由jquery和css3实现的超级梦幻的背景效果.绿色的小原点由远到近,由近到远一种飞跃效果.效果非常好看,我们一起看下效果图: 在线预览   源码下载 我们一起看下实现的代码. ...

  7. 三维动画形变算法(Linear rotation-invariant coordinates和As-Rigid-As-Possible)

    在三维网格形变算法中,个人比较喜欢下面两个算法,算法的效果都比较不错, 不同的是文章[Lipman et al. 2005]算法对控制点平移不太敏感.下面分别介绍这两个算法: 文章[Lipman et ...

  8. 三维动画形变算法(Gradient-Based Deformation)

    将三角网格上的顶点坐标(x,y,z)看作3个独立的标量场,那么网格上每个三角片都存在3个独立的梯度场.该梯度场是网格的微分属性,相当于网格的特征,在形变过程中随控制点集的移动而变化.那么当用户拖拽网格 ...

  9. 三维动画形变算法(Laplacian-Based Deformation)

    网格上顶点的Laplace坐标(均匀权重)定义为:,其中di为顶点vi的1环邻域顶点数. 网格Laplace坐标可以用矩阵形式表示:△=LV,其中,那么根据网格的Laplace坐标通过求解稀疏线性方程 ...

随机推荐

  1. ecshop ad调用指定广告的方法 邓士鹏

      在include/lib_goods.php文件下面新增:function getads($cat,$num){$time = gmtime();$sql = "SELECT * FRO ...

  2. BZOJ——T 1053: [HAOI2007]反素数ant

    http://www.lydsy.com/JudgeOnline/problem.php?id=1053 Description 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6) ...

  3. hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

    题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...

  4. STM32学习之路-LCD(4)&lt;显示字符&gt;

    昨晚疯狂的打了一夜的LOL,感觉L多了,今天一天精神萎靡.还是继续把显示字符给看了,可是在犹豫要不要写这篇文章 事实上写的东西也就是copy别人家的代码,不想写那么多,就记录下自己困惑的地方吧.也许改 ...

  5. [LeetCode]Wildcard Matching 通配符匹配(贪心)

    一開始採用递归写.TLE. class Solution { public: bool flag; int n,m; void dfs(int id0,const char *s,int id1,co ...

  6. HDU 2027 汉字统计

    汉字统计 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  7. Spring JDBC数据库开发

    针对数据库操作,Spring框架提供了JdbcTemplate类. 1.Spring JDBC的配置 创建配置文件applicationContext.xml,添加如下代码: <!--配置数据源 ...

  8. [Linux]非常方便的上传下载文件工具rz和sz

     linux上非常方便的上传下载文件工具rz和sz (本文适合linux入门的朋友) [一般用于SecureCRT ssh中使用] █ 法一:直接用yum安装lrzsz(推荐) yum insta ...

  9. bzoj5029: 贴小广告&&bzoj5168: [HAOI2014]贴海报

    以后做双精题请至少先跑个数据...输入都不一样... 做法就是离散化大力线段树. 记得在x+1和y-1插点 看这个数据: 1000 121 10050 8080 9950 981 56100 2002 ...

  10. nginx FastCGI模块(FastCGI)配置

    http://www.howtocn.org/nginx:nginx%E6%A8%A1%E5%9D%97%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8C%E4%B8%AD%E6 ...