角度计算
在 AR 中,我们经常看到夹角的计算,常见的有让物体朝向手机,或者箭头从 A 点指向 B 点,一般都需要用到 atan2 等函数:
atan和atan2都是反正切函数,如:有两个点 point(x1,y1), 和 point(x2,y2);
那么这两个点形成的斜率的弧度计算方法分别是:
float radian = atan( (y2-y1)/(x2-x1) );
或
float radian = atan2( y2-y1, x2-x1 );
转化为角度:
float degree = arc / pi * 180
atan 和 atan2 区别在于:
1,参数的填写方式不同;
2,atan2 的优点在于x2-x1等于0时依然可以计算,但是atan函数除零会出错;
但实际上,我们可以不用这么麻烦,使用 ARKit 提供的 lookAt 方法可以让我们方便地完成“指向”操作。
lookAt 与 simdLookAt
lookAt
操作可以让一个 node “指向”某个位置,默认的情况下,就是以 z 轴的正方向来对着目标位置。
如下图,飞机的头部指向了 z 轴的正方向。
localFront
可以控制自身的旋转,比如让自己(SCNNode 对象)的 x 轴正方向朝向目标,只需要设置 localFront
为 (1,0,0) 就可以了。那么
worldUp
是干什么的呢?其实它是用来限制意外旋转的。就是说,当物体 A lookAt 物体 B 的时候,物体 A 有可能会绕着 A-B 点的连线旋转,所以需要指定worldUp
来限制。指定后在旋转时会尽可能减小与worldUp
方向的夹角,所以就不会绕 A-B 连线随意旋转了。
比如,这种情况,也是满足 lookAt 条件的,但是飞机却歪了,这不是我们想要的,所以worldUp
就是为了防止出现这种情况的。
如果采用下面的代码,指定了worldUp
,就不会有这种情况了:
// 延迟 2 秒,将飞机的(0,0,-1)即尾部朝向手机,同时指定 up 为世界坐标的 y 轴正方向(0,1,0),这样飞机就不会歪了
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.shipNode simdLookAt:self.sceneView.pointOfView.simdPosition up:simd_make_float3(0, 1, 0) localFront:simd_make_float3(0, 0, -1)];
});
simd 版本作用也是一样的。
- (void)lookAt:(SCNVector3)worldTarget;
- (void)lookAt:(SCNVector3)worldTarget up:(SCNVector3)worldUp localFront:(SCNVector3)localFront;
// 对应的 simd 版本
- (void)simdLookAt:(simd_float3)worldTarget;
- (void)simdLookAt:(simd_float3)worldTarget up:(simd_float3)worldUp localFront:(simd_float3)localFront;
SCNLookAtConstraint 与 SCNBillboardConstraint
这两个操作是约束,类似 AutoLayout 的效果,可以实时更新,不管目标怎么动,也不管自身怎么动,都一直起作用。
SCNBillboardConstraint 就是俗称的“广告牌”效果,让物体始终正面对着相机(即手机)的位置,同时保持 y 轴不变(竖直方向不变,只水平旋转)。当然,也可以调整freeAxes
让它保持 x 轴不变,绕 x 轴旋转。
// 始终指向相机位置
SCNBillboardConstraint *con = [SCNBillboardConstraint billboardConstraint];
con.freeAxes = SCNBillboardAxisY;
self.shipNode.constraints = @[con];
可以看到,当左右水平移动时,飞机头部始终朝向相机方向;当手机从高处或低处看时,飞机保持竖直方向,这就是广告牌效果:
需要注意的是,广告牌会让旋转轴自动变竖直。如下图:SCNLookAtConstraint 就没有那么多限制,可以自由旋转。同时,它也有localFront
和worldUp
,可以调整朝向的姿态并防止意外的旋转;另外它还有targetOffset
来让你指向目标的某一侧;此外gimbalLockEnabled
可以用来处理万向结锁问题。
惟一不同的是,SCNLookAtConstraint 默认是用 z 轴负方向(即飞机尾部)来指向目标的。