SCNCamera 在 SceneKit 和 ARKit 中有什么不同?

1,420 阅读4分钟

在 ARKit 和 SceneKit 开发中,相机位置都是用 SCNView 对象的 pointOfView 结点来表示的,相机的具体参数则在scnView.pointOfView.camera中。

我们先来看看 SCNCamera 中几个不太好理解的参数,然后再来看看它在 SceneKit 和 ARKit 中有什么不同。

SCNCamera 重要参数

这是苹果官网上的说明图片,表示了相机一些参数的含义,但是这张图已经过时了,现在苹果已经废弃了 xFOVyFOV 这种表示显示范围的方法,统一用 fieldOfView 来表示了。

  • projectionDirection:默认值SCNCameraProjectionDirectionVertical
    这个参数是决定下面 fieldOfView 方向的,两者配合使用。共有水平和竖直两种选择,默认是按竖直方向计算 fieldOfView。比如,视图宽高比为 4:3,竖直方向的 fieldOfView 为 60 度,那么水平自然就是 80 度;视图宽高比为 16:9,竖直方向的 fieldOfView 为 60 度,那么水平自然就是 106 度。

  • fieldOfView:默认 60 度
    以前的 xFOVyFOV 可以自己随意调整,现在不行了。现在你只有通过 fieldOfView 决定其中一个(哪一个由projectionDirection的值决定,默认是竖直),另一个方向会根据视图比例自动计算。苹果举例说明:当手机屏幕是16:9并竖屏,SCNView 全屏铺满时宽高比9:16,projectionDirection 默认竖直,而 fieldOfView 是 60 度时,水平视角度数是 33.75 度。这个角度就是保持竖起方向 60 度的情况,根据 16:9 的比例,换算出的水平视角。

  • sensorHeight:默认值和手机镜头有关,常见是 24mm
    这个值一般不去调整,保持默认就好了。如果非要调整的话,会导致 fieldOfView 发生变化,sensorHeight 变大则 fieldOfView 变小,画面显示的物体则会变大。

  • focalLength:默认值和手机有关,常见是 20.784610748291016mm
    这个值,其实一开始是根据 sensorHeightfieldOfView 的默认值计算出来的。调整这个参数,会改变 fieldOfView 的值。

  • zNear:ARKit 默认 0.001m,SceneKit 默认 1.0m
    能看到的最近距离

  • zFar:ARKit 默认 1000m,SceneKit 默认 100m
    能看到的最远距离

从上面可以看出:fieldOfViewsensorHeightfocalLength 这三个参数是密切相关的,调整任意一个可能会影响其他的。那一般我们需要时应该怎么调整呢?答案是调整 fieldOfView

fieldOfView 是最终决定显示效果的参数,也是人理解起来最直观的参数,所以我们只需要调整它,就能达到我们想要的效果。但实际上,从本质来说,如果只调整 fieldOfView,那最终会导致 focalLength 发生变化,从而影响投影矩阵和显示效果。

三者的关系如下图:

SceneKit 与 ARKit 中的不同

经过实践,我发现 SceneKit 与 ARKit 中 Camera 的不同,除了 zNearzFar外,最主要是对projectionDirection的认定不同。

SceneKit 中的视图方向是跟随手机旋转方向的,当手机方向变化时,永远保持竖直方向的视角为 60 度,即使视图变宽了,也不会改变显示的大小。
而 ARKit 比较复杂,当视图的高度占满屏幕时,只缩小宽度没什么变化,这和 SceneKit 是一样的。当宽度高度都改变时,表现也和 SceneKit 相同。而当高度较小时 ARKit 视图的宽度变大,看到的物体也会变大!!!

这到底是什么意思?我们来看几张对比图就知道了。下图的 demo 中一个有背景的是 ARSCNView,另一个黑色背景的是 SCNView。两者的 3D 模型位置、大小相同,相机参数包括位置也是相同的。先看竖屏时的情况:

再看横屏时的情况:

可以看到的是:SceneKit 中的fieldOfView严格遵守了约定的角度和方向,始终严格按照竖直方向来计算,当手机横屏切换时,会按照新的竖直方向来计算;
而 ARKit 就复杂了很多,宽度和高度似乎都会影响。

经过我的观察,发现:ARKit 似乎会按照最长的那个边进行计算,如果最长的那条边没有改变,画面就会保持原先的大小;而最长的那条边变短时,画面就会按比例缩小。

可能大家会问,为什么不打印一下 ARKit 中 SCNCamera 的参数呢?其实我试过了,不管画面如何变化,ARKit 中 SCNCamera 的参数永远也不会变,根本看不出来具体是多少。。。。。

总结

SCNCamera 的参数在 ARKit 和 SceneKit 中表现并不相同,产生的效果也有一点差异。最主要差异是:SceneKit 会遵守设置的方向和角度来设置 FOV,并会根据手机屏幕旋转来更新;而 ARKit 无视手机屏幕旋转,也不遵守projectionDirection的设置,似乎会按视图最长边来设置 FOV。