
1.2 Camera类用法详解
在了解了如何使用Camera类以后,再来看看Camera类具有的操作Camera的函数。本节会对这些函数进行简单的讲解,而后面还会使用具体的示例进行讲解。
1.2.1 平移
平移函数如下:

和2D平移函数类似,只不过本节介绍的这个函数中多出了一个维度,从只能在2D平面上平移变为在3D空间内平移。
参数介绍如下。
●float x:X轴上的偏移量。
●float y:Y轴上的偏移量。
●float z:Z轴上的偏移量。
1.沿X轴平移
如果我们将1.1节示例中的旋转代码改为:

此时,完整的onDraw函数代码如下:

完整的onDraw函数代码仅在本节中列出,后续的相关代码中都将只改变camera.translate(mProgress,0,0);这行代码。
效果如图1-10所示。
可能有的读者会想,camera.translate(5,0,0)是什么意思呢?是Camera正向移动5 px,还是物体正向移动5 px呢?
其实Camera类的各个函数操作的并不是摄像机,而是物体,我们看到的是移动物体之后屏幕上图像的变化。
比如,camera.translate(5,0,0)表示的移动与拍摄过程如图1-11所示。

图1-10

扫码查看动态效果图

图1-11

扫码查看彩色图
从图1-11可以看到,在物体向右移动之后,屏幕上形成的二维图像(如黑框所示)也向右移动了。这刚好与效果图中的效果相对应。
2.沿Y轴平移
同样地,如果沿着Y轴移动,可以将示例中的移动代码改为:

对应的效果如图1-12所示。

图1-12

扫码查看动态效果图
原理如图1-13所示。

图1-13

扫码查看彩色图
可以看到,在物体向上移动后,屏幕上对应的二维图像也向上移动了(黄框移动到黑框位置),这就解释了为什么会出现效果图中的效果。
3.沿Z轴平移
(1)沿Z轴正方向平移
沿Z轴平移的效果比较特殊,它能实现图像的放大与缩小,我们先来看看效果。如果把示例中的移动代码改为:

即沿Z轴的正方向从1 px移动到360 px,效果如图1-14所示。

图1-14

扫码查看动态效果图
在图1-14中,首先,随着物体沿Z轴正方向移动距离的增大,图像逐渐变小。其次,虽然图像在变小,但图像左上角在屏幕上的位置始终不变。下面就分别讲解这两个现象的原因。
随着物体沿Z轴正方向移动距离的增大,图像逐渐变小,原理图如图1-15所示。
从图1-15可以看出,源图像在屏幕上对应的是黑框位置,在图像向Z轴正方向移动后,其在屏幕上对应的是黄框位置。随着图像沿Z轴离屏幕越来越远,图像和Camera的连线与屏幕的交点所形成的图像越来越小。这一点不难理解,就像图1-16中的铁轨。

图1-15

扫码查看彩色图

图1-16
我们都知道两条铁轨之间的距离是完全相同的,但随着铁轨的远离,两条铁轨之间的距离看起来越来越小。
关于第二个现象,为什么屏幕上图像的左上角位置始终不变呢?
前面曾经提到过Camera的位置在3D坐标系的(0,0,-576)处,其投影到View上位于View的左上角处,所以当图像远离Camera时,都是以左上角为原点来缩小的,如图1-17所示。

图1-17

扫码查看彩色图
(2)沿Z轴负方向平移
相反地,如果我们让物体沿Z轴负方向移动,即将示例中的移动代码改为:

需要注意,为了更好地演示效果,这里不仅取了负号而且乘以了2,Z轴负方向的最大移动距离变为-720,效果如图1-18所示。

图1-18

扫码查看动态图
从图1-18可以看出,随着物体向Z轴负方向移动,图像逐渐变大,但在达到一定的大小后,图像却突然不见了。
在这里读者可能会产生两个疑问:
●为什么图像会变大呢?
●为什么图像会消失呢?
首先,对于第一个问题,随着物体向Z轴负方向移动,图像逐渐变大,这是因为Camera在Z轴上位于-576 px处,所以当物体向Z轴负方向移动时,其实是距离Camera越来越近了,当然图像会越来越大。而且,当图像大到一定程度时,我们就只能看到其中一部分。同样地,由于Camera投影到View上时位于View的左上角,所以图像在变大的过程中仍然是以左上角为原点变大的。
但当物体移动到Camera位置时,就看不到物体了,这时的表现是屏幕上没有对应的图像。这一点其实也很好理解,你可以拿着一个水杯,将其由远及近地靠近你的一只眼睛,当水杯跟你的眼睛平齐时,即水杯在耳朵位置时,你就看不到水杯了。
到这里,有关3D效果中的平移就介绍完了。相对而言,3D效果中的平移理解起来困难一些,我们可以用生活中的例子来演示这个效果。比如,你把眼睛当作Camera,用一个物体来模拟View,在眼前来回移动这个物体,这样就可以辅助理解camera.translate相关参数的意义与效果。
1.2.2 旋转
旋转是3D动画的核心效果,但因为单个View是没有厚度的,所以通过它制作出来的旋转效果并不算是真正的3D效果,而是伪3D效果。但我们可以像开始介绍的StereoView控件一样,通过多个View的组合旋转来实现看起来更真实的3D效果。


旋转的函数总共有4个,其中rotate(float x,float y,float z)可以同时指定3个维度的旋转角度,但它是API 12后才加入的新函数,而其他3个函数都只能一次绕一个维度旋转,它们在API 1时就已经存在。当然,如果你需要在API 12之前同时绕多个维度旋转,可以多次调用旋转函数。
1.绕X轴旋转
将示例中的Camera操作代码改为:

效果如图1-19所示。

图1-19

扫码查看动态图
因为3D坐标系的原点位于View的左上角,所以,当图像绕X轴旋转时,是直接绕X轴旋转的。但是,在旋转到90°时,就看不到图像了。
首先,绕X轴的正方向旋转,如图1-20所示。

图1-20

扫码查看彩色图
图1-20展示了X、Y、Z轴各自的旋转正方向,在后面的demo效果图中,我们也可以具体查看。
旋转过程中的示意图如图1-21所示。

图1-21

扫码查看彩色图
图1-21显示了旋转前和旋转后屏幕上图像的示意图。原始图像是黑框,旋转后的图像是黄框。大家也可以拿一张纸在摄像机前旋转并模拟这个过程,以更好地理解旋转功能。
因为Camera的位置在(0,0,-576)处,也就是说Camera在3D坐标系Z轴上的-576 px处,当图像旋转到90°时,Camera与图像在一个平面上,这时屏幕上的效果就是在图像旋转90°以后(大于90°且小于270°)什么也看不到了。
2.绕Y轴旋转
将demo的代码改为:

效果如图1-22所示。

图1-22

扫码查看动态效果图
从图1-22可以看出,图像默认是绕Y轴旋转的,在旋转90°以后(大于90°且小于270°),Camera与图像在一个平面上,图像与屏幕的View区域没有交集,所以不显示任何内容。
3.绕Z轴旋转
将demo的代码改为:

效果如图1-23所示。

图1-23

扫码查看动态效果图
同样地,以坐标系原点为中心绕Z轴旋转。在图像旋转90°以后(大于90°且小于270°),图像就与View没有交集了,所以屏幕上没有任何图像,即屏幕上不显示任何内容。
4.调整旋转中心点
从上面的效果可以看出,默认都是围绕X、Y、Z轴旋转的,有时我们想实现翻转卡片的效果,扫码查看右侧效果图。

扫码查看动态效果图
在这个示例中,很明显,旋转中心点变为了图像中心点。那么怎么调整旋转中心点呢?
如果我们想将默认的3D坐标系中心点调整到(x,y)位置,只需要在获取矩阵后,通过下面的代码将中心点位置调整到(x,y)位置:

有关这段代码,我们会放在第2章讲解位置矩阵时详细说明,在这里,大家只要会用就行。但需要特别注意的一点是,这里是通过后期改变矩阵的值来改变旋转中心点的,并没有改变原始Camera的位置,Camera在View所在的2D坐标系中的投影位置依然在View的左上角(0,0)处。
比如,我们修改一下demo中的代码,将旋转中心点改为图像中心点:


这段代码比较好理解,只是在操作了Camera以后,通过camera.getMatrix(matrix)获取对应操作的matrix数组,然后通过调整旋转中心点的代码,将旋转中心点调整到图像中心点。
调整中心点后绕X轴旋转的效果如图1-24所示。

图1-24

扫码查看动态效果图
同样地,调整旋转中心点到图像中心点后,绕Y轴旋转的效果如图1-25所示。

图1-25

扫码查看动态效果图
最后,再来看看在调整旋转中心点到图像中心点后,绕Z轴旋转的效果如图1-26所示。

图1-26

扫码查看动态效果图
1.2.3 改变Camera的位置
在API≥15时,Camera类新增了几个函数来获取和改变Camera的位置:

1.Camera的坐标单位
这里有一个非常奇怪的问题,那就是Camera的坐标单位不是px,而是其每个单位表示72 px。
这个单位的表示函数可以在Android底层的图像引擎Skia中找到。在Skia中,Camera的位置单位是英寸,1英寸可换算为72 px,而在Android中把这个换算函数照搬了过来。这个换算函数是固定的,不会随着手机分辨率的改变而变化。
比如,我们在onDraw中通过日志获取Camera默认的位置信息,代码如下:

日志如图1-27所示。

图1-27
可以看到,在默认的情况下,Camera的位置在View左上角外部,并且位于Z轴的-576 px位置(-8×72=-576)。
2.更改Camera的位置
我们可以通过void setLocation(float x,float y,float z)来设置Camera的位置。但需要注意的是,设置位置时,一个单位相当于72 px,所以这里的参数值需要做单位变换。
假如我们将Camera的位置从默认的左上角外部移到图像中心点外部,代码如下:


从上面的代码可以看出,我们通过getWidth()/2得到了中心点位置的坐标,然后除以72换算成英寸单位。需要注意的是,Y轴的正方向是沿着屏幕向上的,所以,我们若要让Camera向下移动,需要将移动距离设置为-centerY。
效果如图1-28所示。

图1-28

扫码查看动态效果图
这里有两点需要注意:
(1)当将Camera移到图像中心点外部以后,图像只显示右下角的四分之一,这是为什么呢?
(2)当将Camera移到图像中心点外部以后,图像的旋转中心仍在图像的左上角。Camera的位置可以通过setLocation函数来改变,而如果我们想要改变3D坐标系的位置,则需要通过Matrix操作来实现。
下面的示意图展示了Camera从图像的左上角外部移到图像中心点外部的过程,如图1-29所示。

图1-29

扫码查看彩色图
在图1-29中,红色框表示当Camera在图像左上角外部时显示的图像范围,此时,整个图像都可以得到显示;绿色框表示在Camera移到图像中心点外部以后,显示范围随着Camera移动的情况。从这个示意图可以看出,在Camera移到图像中心点外部以后,只能显示右下角部分的图像,与代码运行效果相同。
Camera的移动与图像显示理解起来相对有些难度,大家可以把A4纸当作View,把手机摄像头当作Camera,通过移动手机摄像头来查看手机屏幕中显示内容的变化情况,两者的原理完全相同。