ARKit是蘋果在WWDC2017推出的一個(gè)用于實(shí)現(xiàn)增強(qiáng)現(xiàn)實(shí)(AR)的框架;開發(fā)者能夠使用它來(lái)快速的完成基本的AR功能開發(fā)。
軟硬件支持范圍
- 硬件:A9處理器及以上(iPhone 6s)
- 軟件:Xcode 9.0、iOS 11 及以上
必須同時(shí)具備上述條件。
需要其他什么
- 相應(yīng)的3D模型(dae格式)
- SceneKit、SpriteKit或Metal相關(guān)基礎(chǔ)知識(shí)(用做渲染)
ARKit的類結(jié)構(gòu)圖
ARKit如何工作
假設(shè),我們需要實(shí)現(xiàn)的AR功能是:通過(guò)app在現(xiàn)實(shí)世界的桌子上放置一個(gè)杯子。我們需要通過(guò)怎么的步驟來(lái)實(shí)現(xiàn)這個(gè)功能呢?或者說(shuō)ARKit是怎么實(shí)現(xiàn)這樣的功能的呢?
總結(jié)來(lái)說(shuō)有三個(gè)重要的部分:
- 1、Tracking(實(shí)時(shí)捕捉周圍的信息,并處理生成相應(yīng)格式的數(shù)據(jù))
- 2、Scene Understanding(理解當(dāng)前的場(chǎng)景,并找到合適的放置虛擬模型的位置)
- 3、Rendering(渲染并展示)
Tracking
Tracking是ARKit的核心功能,它負(fù)責(zé)實(shí)時(shí)追蹤設(shè)備。
特性:
- World tracking(通過(guò)World tracking能夠得到設(shè)備在現(xiàn)實(shí)世界的相對(duì)位置。)
- Visual inertial odometry(同時(shí)使用攝像機(jī)捕捉的圖像和設(shè)備運(yùn)動(dòng)狀態(tài)來(lái)得到一個(gè)精確的設(shè)備位置和方向)
- No external setup(無(wú)需外部的設(shè)置)
ARCamera中有兩個(gè)屬性trackingState和trackingStateReason,開發(fā)者可以根據(jù)這兩個(gè)屬性的值,對(duì)用戶進(jìn)行相應(yīng)的提示。
Scene Understanding
Scene Understanding就是理解設(shè)備周圍環(huán)境的特征;例如平面檢測(cè),平面檢測(cè)就是檢測(cè)并分析出設(shè)備周圍環(huán)境中的平面。
特性:
- Plane detection(平面檢測(cè))
- Hit-testing(用于尋找現(xiàn)實(shí)世界的點(diǎn))
- Light estimation(根據(jù)現(xiàn)實(shí)世界的環(huán)境來(lái)改變虛擬模型光照)
Rendering
渲染。
特性:
- Easy integration(集成簡(jiǎn)單)
- AR views(實(shí)現(xiàn)了大部分的渲染工作)
- Custom rendering(可以自定義渲染)
ARKit的使用
ARKit是基于會(huì)話(ARSession)的一套API,ARSession處理包含Tracking、Scene Understanding以及渲染時(shí)需要的數(shù)據(jù)在內(nèi)的許多復(fù)雜的進(jìn)程。
ARSession和ARConfiguration
ARSession內(nèi)部使用了AVCaptureSession和CMMotionManager來(lái)獲取圖像信息和設(shè)備運(yùn)動(dòng)信息;根據(jù)ARConfiguration指定的Tracking類型來(lái)合成數(shù)據(jù),最終輸出ARFrame。每個(gè)ARFame包含了渲染需要的所有信息,可以把它理解成對(duì)應(yīng)時(shí)間的快照。想要獲得ARFrame有兩種方式:
- 1、通過(guò)ARSession的委托,在每一幀更新時(shí)處理:
- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame;
- 2、通過(guò)currentFrame屬性,在需要時(shí)主動(dòng)獲取當(dāng)前幀:
@property (nonatomic, copy, nullable, readonly) ARFrame *currentFrame;
ARConfiguration決定了Tracking類型;基類ARConfiguration有三個(gè)維度的追蹤,也即是設(shè)備的方向;子類ARWorldTrackingConfiguration擁有6個(gè)維度的追蹤,它即包含了設(shè)備的方向,也包含了設(shè)備在現(xiàn)實(shí)世界中的相對(duì)位置。
可以使用ARConfiguration的類屬性isSupported來(lái)判斷,當(dāng)前設(shè)備是否支持該模式的追蹤。@property(class, nonatomic, readonly) BOOL isSupported;
基本使用流程
想要使用ARKit,首要的步驟就是創(chuàng)建ARSession;其次,選用相應(yīng)的ARConfiguration來(lái)決定Tracking的類型;然后使會(huì)話運(yùn)行;之后的操作就是放置虛擬模型并渲染顯示。總結(jié)如下:
- 1、創(chuàng)建ARSession對(duì)象
- 2、創(chuàng)建相應(yīng)的ARConfiguration對(duì)象
- 3、Run Session
- 4、對(duì)場(chǎng)景做相應(yīng)的檢測(cè),找到防止虛擬模型的合適位置
- 5、添加模型
- 6、渲染顯示
上文中說(shuō)到SceneKit和SpriteKit,在ARKit中,有兩個(gè)類:ARSCNView、ARSKView是分別繼承自上述框架中的SCNView、SKView;ARKit結(jié)合這兩個(gè)框架為開發(fā)者提供了更加簡(jiǎn)單的使用過(guò)程,可以將ARSCNView(3D)、ARSKView(2D)理解成兩種渲染引擎(一個(gè)負(fù)責(zé)3D、一個(gè)負(fù)責(zé)2D),只要開發(fā)者選用其中的一種,那么將追蹤得到的幀數(shù)據(jù)就會(huì)自動(dòng)渲染并顯示到相應(yīng)的View上;對(duì)于特殊需求,也可以使用Metal來(lái)完成自定義的渲染。本文中都是使用ARSCNView來(lái)顯示場(chǎng)景的。
ARkit以及SceneKit的類關(guān)系圖(圖片摘取于:u013263917博客)
使用ARSCNView時(shí)啟動(dòng)Tracking的代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
// 設(shè)置view
self.sceneView.delegate = self;
self.sceneView.automaticallyUpdatesLighting = NO;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (ARWorldTrackingConfiguration.isSupported) {
// 防止熄屏
[UIApplication sharedApplication].idleTimerDisabled = YES;
// 創(chuàng)建一個(gè)能夠檢測(cè)水平面的configuration
ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new];
configuration.planeDetection = ARPlaneDetectionHorizontal;
// 運(yùn)行
[self.sceneView.session runWithConfiguration:configuration options:ARSessionRunOptionResetTracking|ARSessionRunOptionRemoveExistingAnchors];
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 暫停session
[self.sceneView.session pause];
}
在完成上述的代碼之后,就能夠在ARSCNView上看到攝像機(jī)捕捉的景象。接下來(lái)就是找到合適的位置來(lái)放置虛擬的模型,如找一個(gè)平面,或者一個(gè)點(diǎn)。
對(duì)于平面,可以使用ARWorldTrackingConfiguration的屬性planeDetection來(lái)設(shè)置(目前只支持水平面),在檢測(cè)到平面之后,ARKit會(huì)自動(dòng)向Session中添加一個(gè)ARAnchor;ARAnchor代表現(xiàn)實(shí)世界中的一個(gè)位置,每一個(gè)添加到Session中的ARAnchor都會(huì)有一個(gè)與之對(duì)應(yīng)的節(jié)點(diǎn)(SCNNode),于是我們就可以在這個(gè)節(jié)點(diǎn)上添加模型(子節(jié)點(diǎn))。
如果想要將模型添加到指定的空間點(diǎn),ARKit為ARFrame提供了Hit-testing。方法聲明為:
- (NSArray<ARHitTestResult *> *)hitTest:(CGPoint)point types:(ARHitTestResultType)types;
Hit-testing就是從手機(jī)模擬發(fā)射一條射線去,尋找與現(xiàn)實(shí)世界的交點(diǎn)。
其中point參數(shù),為當(dāng)前追蹤的圖片的相對(duì)位置,左上角是(0,0)右下角是(1,1);這個(gè)參數(shù)可以使用手勢(shì)來(lái)輸入。ARHitTestResultType是一個(gè)枚舉,決定用什么樣的方式去Hit-testing。
typedef NS_OPTIONS(NSUInteger, ARHitTestResultType) {
/** 返回射線上與特征點(diǎn)最接近的點(diǎn) */
ARHitTestResultTypeFeaturePoint = (1 << 0),
/** 從當(dāng)前幀中確定一個(gè)平面,然后返回與平面的交點(diǎn) */
ARHitTestResultTypeEstimatedHorizontalPlane = (1 << 1),
/** 將當(dāng)前已經(jīng)檢測(cè)到的平面看作無(wú)限大的平面,然后返回與這些平面的交點(diǎn) */
ARHitTestResultTypeExistingPlane = (1 << 3),
/** 僅僅返回當(dāng)前平面范圍內(nèi)的交點(diǎn) */
ARHitTestResultTypeExistingPlaneUsingExtent = (1 << 4),
} NS_SWIFT_NAME(ARHitTestResult.ResultType);
代碼中結(jié)合手勢(shì)使用Hit-testing
- (IBAction)tapAction:(UITapGestureRecognizer *)sender {
// 獲取當(dāng)前幀
ARFrame *frame = self.sceneView.session.currentFrame;
if (frame) {
// 根據(jù)手勢(shì)設(shè)置點(diǎn)的位置
CGPoint location = [sender locationInView:self.sceneView];
CGSize size = self.sceneView.bounds.size;
CGFloat x,y;
x = location.x/size.width;
y = location.y/size.height;
// hit-testing
NSArray<ARHitTestResult *> * results = [frame hitTest:CGPointMake(x, y) types:ARHitTestResultTypeEstimatedHorizontalPlane];
if (results.count > 0) {
// 將得到的第一個(gè)點(diǎn)添加到session
ARHitTestResult *res = [results firstObject];
ARAnchor *anchor = [[ARAnchor alloc] initWithTransform:res.worldTransform];
[self.sceneView.session addAnchor:anchor];
}
}
}
添加hit-testing的Anchor后,可以在對(duì)應(yīng)的委托方法中,添加相應(yīng)的虛擬物體的模型
- (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
// 加載模型
SCNScene *candle = [SCNScene sceneNamed:@"candle.scn" inDirectory:@"Models.scnassets/candle" options:nil];
SCNNode *candleNode = [[candle.rootNode childNodes] firstObject];
// 添加到對(duì)應(yīng)的節(jié)點(diǎn)上
[node addChildNode:candleNode];
}
效果圖如下:
至此已經(jīng)完成了一個(gè)基本的AR應(yīng)用,如果需要完成更加復(fù)雜的應(yīng)用,還需要掌握很多關(guān)于SceneKit的知識(shí)(如模型的動(dòng)畫、坐標(biāo)系的轉(zhuǎn)換等)以及加入更復(fù)雜的邏輯和算法。
如何向一個(gè)工程中引入ARKit
- 1、創(chuàng)建一個(gè)自定義個(gè)ViewController,并為其加上一個(gè)ARSCNView,將這個(gè)view設(shè)置為ViewController的一個(gè)屬性
- 2、初始化session和configuration
- 3、加載虛擬模型及其他操作
模型的導(dǎo)入
- 1、創(chuàng)建一個(gè)文件夾,用"xxx.scnassets"的方式命名;
- 2、將該文件夾拖入工程;
- 3、將.dae格式的模型拖入該文件夾(在工程中拖入),然后選中Edit菜單,將其轉(zhuǎn)換成scn文件;
轉(zhuǎn)換后的結(jié)果:
- 4、加載文件,獲取需要的節(jié)點(diǎn)。
// 加載場(chǎng)景
SCNScene *scene = [SCNScene sceneNamed:@"chameleon.scn" inDirectory:@"Models.scnassets" options:nil];
// 獲取子節(jié)點(diǎn)
NSArray *childNodes = [scene.rootNode childNodes];