iOS借助ARKit实现六自由度的VR

144
作者 二月的大胡子
2017.07.25 20:31* 字数 882

Part1.效果展示

效果展示


Demo.gif
Demo.gif

demo02.gif
demo02.gif

图展示了『前后左右上下+头部随动』即六自由度的VR效果。

工程源码:
https://github.com/WorkerAmo/ARKitPlusVR

已被录入 https://github.com/olucurious/awesome-arkit

Part2.原理解析

涉及的库

ARKit & SceneKit

原理

github上有Google CardBoard供大家使用,也有早期某好人开源后不更新的版本。
我接触SceneKit发现可以便捷的实现VR效果,当然需要舍弃一部分内容。
个人以为,VR项目中核心组成有三:渲染引擎,九轴算法,反畸变算法。在此处我们可以基本舍弃反畸变算法与九轴算法,依靠SceneKit实现渲染部分。

层级关系.png
层级关系.png

直接使用Xcode9beta在ARKit新建工程Demo的基础上添加ARSCNView双屏即可。

// retrieve the SCNView
SCNView *scnViewLeft = [[SCNView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width*0.5, self.view.frame.size.height)];
scnViewLeft.pointOfView = cameraNodeLeft;
scnViewLeft.scene = scene;
scnViewLeft.backgroundColor = [UIColor blackColor];
[self.sceneView addSubview:scnViewLeft];
    
SCNView *scnViewRight = [[SCNView alloc]initWithFrame:CGRectMake(self.view.frame.size.width*0.5, 0, 0.5*self.view.frame.size.width, self.view.frame.size.height)];
scnViewRight.pointOfView = cameraNodeRight;
scnViewRight.scene = scene;
scnViewRight.backgroundColor = [UIColor blackColor];
[self.sceneView addSubview:scnViewRight];

关于自由度

目前iPhone上可以下载到的VRAPP基本都是三自由度,即围绕XYZ三轴心旋转实现camera跟随头部转动的效果。用户无法自由移动从而接近或沿四周观察物体。手机在不借助外接设备的情况下实现VR空间定位的产品目前基本没有。但是借助ARKit,我们可以实现且误差估计在十厘米左右。

六轴自由度.jpg
六轴自由度.jpg

图示为六自由度,三自由度为去除up/down,left/right,forward/back三轴的剩余部分。

Part3 VR部分的实现

Camera设置

在此Demo中需要注意的就是camera的设置。与一般游戏开发不同的是,我们这里需要2个camera,分别用于左右眼内容显示。


双目视差(来自网络,侵删).jpg
双目视差(来自网络,侵删).jpg

因为左右眼内容实际是不一样的,所以需要2个camera在增强视差实现立体效果。
考虑到后续需要2个眼睛随着头部转动,会产生位移与旋转,所以我们需要增加一个新的camera作为2个camera的容器。

// Containor Camera. 
_cameraNode = [SCNNode node];
_cameraNode.camera = [SCNCamera camera];
[_cameraNode setPosition:SCNVector3Make(0, 0, 0)];
[scene.rootNode addChildNode:_cameraNode];

// Camera left
SCNNode *cameraNodeLeft = [SCNNode node];
cameraNodeLeft.camera = [SCNCamera camera];
[cameraNodeLeft setPosition:SCNVector3Make(-0.1, 0, 0)];
[_cameraNode addChildNode:cameraNodeLeft];
    
// Camera right
SCNNode *cameraNodeRight = [SCNNode node];
cameraNodeRight.camera = [SCNCamera camera];
[cameraNodeRight setPosition:SCNVector3Make(0.1, 0, 0)];
[_cameraNode addChildNode:cameraNodeRight];

之后针对摄像头组的矩阵直接赋与containor camera即可。

关于摄像头的空间坐标

借助WWDC2017发布的ARKit-ARCamera.transform实现头部随动与空间定位。

#pragma mark - ARSessionDelegate

- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame
{   
    // Retrive the matrix from ARKit - ARFrame - camera.
    _transform = frame.camera.transform;
    [_cameraNode setTransform:SCNMatrix4FromMat4(_transform)];
}

文档提到过ARFrame提供的transform,这里的transform是六自由度的。

/**
 The transformation matrix that defines the camera's rotation and translation in world coordinates.
 */

关于PBR材质

这篇博文很详细,可供参考。
PBR,即Physically based rendering,可以实现很逼真的光影效果。
http://www.jianshu.com/p/b30785bb6c97

至此我们就可以实现文头提供的Demo效果了。虽然误差还是有的,但是毕竟是单目SLAM,是不是已经很厉害了呢。

使用注意点

因为这里空间定位基本依赖于ARKit提供的数据,所以ARKit的精确度直接影响到视觉效果。所以记得使用时记得遵守ARKit提到的运行条件,即https://developer.apple.com/documentation/arkit 提到的

ARKit requires an iOS device with an A9 or later processor.
& 
Understanding Augmented Reality:
Best Practices and Limitations段落中
However, it relies on details of the device’s physical environment that are
not always consistent or are difficult to measure in real time without some
degree of error. To build high-quality AR experiences, be aware of these 
caveats and tips.

简而言之:6S以上的设备,良好的光线环境,避免对着白墙(无法获取特征点)。

这里我分享个没有严谨验证过的适用于ARKit快速稳定的技巧:
斜对着方形区域,水平环绕扫视矩形后继续瞄准沿竖直方向观测,基本就能保持稳定了。


如果您觉得有价值,请在github赏个star,不胜感激。
如果有什么想交流的,欢迎私信。

iOS_ARKit