1. 程式人生 > >ARKit從入門到精通(3)-ARKit自定義實現

ARKit從入門到精通(3)-ARKit自定義實現

1.1-建立一個簡單的工程

  • 1.上一小節中介紹過,ARSCNView的英文UIView的子類的子類,所以從理論上來說,應用我們框架UIKit的英文可以載入AR場景的

0401.png
  • 2.給介面新增一個按鈕開啟AR之旅,建立一個ARSCNViewController:繼承於UIViewController,點選按鈕跳轉到自定義ARSCNViewController

0402.png

1.2-搭建ARKit工作環境

  • 一個完整的ARKit工作環境必須要搭建三個物件:(ARSCNView一旦建立,系統會幫我們建立一個場景Scene ARSessionand 相機),(開啟AR和關閉AR都是靠它),

    ARSessionConfiguration(少了會話追蹤配置,AR會話是無法獨立工作的)

  • 定義全域性屬性

#import "ARSCNViewViewController.h"

//3D遊戲框架
#import <SceneKit/SceneKit.h>
//ARKit框架
#import <ARKit/ARKit.h>

@interface ARSCNViewViewController ()

//AR檢視:展示3D介面
@property(nonatomic,strong)ARSCNView *arSCNView;

//AR會話,負責管理相機追蹤配置及3D相機座標
@property
(nonatomic,strong)ARSession *arSession; //會話追蹤配置:負責追蹤相機的運動 @property(nonatomic,strong)ARSessionConfiguration *arSessionConfiguration; //飛機3D模型(本小節載入多個模型) @property(nonatomic,strong)SCNNode *planeNode; @end
  • 懶載入(筆者個人習慣)ARKit環境
#pragma mark -搭建ARKit環境


//懶載入會話追蹤配置
- (ARSessionConfiguration *)arSessionConfiguration
{
    if
(_arSessionConfiguration != nil) { return _arSessionConfiguration; } //1.建立世界追蹤會話配置(使用ARWorldTrackingSessionConfiguration效果更加好),需要A9晶片支援 ARWorldTrackingSessionConfiguration *configuration = [[ARWorldTrackingSessionConfiguration alloc] init]; //2.設定追蹤方向(追蹤平面,後面會用到) configuration.planeDetection = ARPlaneDetectionHorizontal; _arSessionConfiguration = configuration; //3.自適應燈光(相機從暗到強光快速過渡效果會平緩一些) _arSessionConfiguration.lightEstimationEnabled = YES; return _arSessionConfiguration; } //懶載入拍攝會話 - (ARSession *)arSession { if(_arSession != nil) { return _arSession; } //1.建立會話 _arSession = [[ARSession alloc] init]; //2返回會話 return _arSession; } //建立AR檢視 - (ARSCNView *)arSCNView { if (_arSCNView != nil) { return _arSCNView; } //1.建立AR檢視 _arSCNView = [[ARSCNView alloc] initWithFrame:self.view.bounds]; //2.設定檢視會話 _arSCNView.session = self.arSession; //3.自動重新整理燈光(3D遊戲用到,此處可忽略) _arSCNView.automaticallyUpdatesLighting = YES; return _arSCNView; }

1.3-開啟AR掃描

  • 我們只需要先將AR檢視新增到當前的UIView中,然後開啟AR會話即可開始我們的AR之旅
    • ***這裡需要特別注意的是,最好將開啟ARSession 的程式碼放入viewDidAppear而不是viewDidLoad 中,這樣可以避免執行緒延遲的問題。開啟ARSession 的程式碼可不可以放入viewDidLoad中呢?答案是可以的,但是筆者不建議大家那麼做***
@implementation ARSCNViewViewController

- (void)viewDidLoad {
    [super viewDidLoad];


    // Do any additional setup after loading the view.
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    //1.將AR檢視新增到當前檢視
    [self.view addSubview:self.arSCNView];
    //2.開啟AR會話(此時相機開始工作)
    [self.arSession runWithConfiguration:self.arSessionConfiguration];

}

1.4倍點選螢幕新增一個3D虛擬物體

  • 預設情況下,節點SCNNode的X / Y / Z位置是(0,0,0),也就是攝像頭所在的位置,每一個ARSession在啟動時,攝像頭的位置就是3D世界的原點,而且這個原點不再隨著攝像頭的移動而改變,是第一次就永久固定的
    • 想要讓飛機顯示在你想要的位置,就需要更加深入的研究ARKit框架,需要了解ARKit的座標系及API,筆者將會在下一小節慢慢介紹

雜記 - 點選螢幕新增飛機

  • (void)touchesBegan:(NSSet <UITouch *>  )touches withEvent:(UIEvent )event 

    // 1.使用場景載入scn檔案(使用3DMax軟體可以建立,這裡系統有一個預設的3D飛機)--------在右側我添加了許多3D模型,只需要替換檔名即可
    SCNScene  scene = [SCNScene sceneNamed:@“Models.scnassets / ship.scn”] ; 
    // 2。獲取飛機節點(一個場景會有多個節點,此處我們只寫,飛機節點則預設是場景子節點的第一個)
    //所有的場景有且只有一個根節點,其他所有節點都是根節點的子節點
    SCNNode 
     shipNode = scene.rootNode.childNodes [0];

    // 3。將飛機節點新增到當前螢幕中
    [self.arSCNView.scene.rootNode addChildNode:shipNode]; 
    }

1.5效果展示

  • 在筆者的Xcode左側已經匯入了好幾個3D模型,只需要修改檔名既可以載入不同的3D模型,注意路徑區別

0403.png
  • 飛機

0404.gif
  • 來張椅子坐一下吧
    • 椅子比較大,我們需要適當調整一下位置

0405.png
0405.gif

1.6-完整程式碼及程式碼下載地址

  • 完整程式碼
#import "ARSCNViewViewController.h"

//3D遊戲框架
#import <SceneKit/SceneKit.h>
//ARKit框架
#import <ARKit/ARKit.h>

@interface ARSCNViewViewController ()

//AR檢視:展示3D介面
@property(nonatomic,strong)ARSCNView *arSCNView;

//AR會話,負責管理相機追蹤配置及3D相機座標
@property(nonatomic,strong)ARSession *arSession;

//會話追蹤配置:負責追蹤相機的運動
@property(nonatomic,strong)ARSessionConfiguration *arSessionConfiguration;

//飛機3D模型(本小節載入多個模型)
@property(nonatomic,strong)SCNNode *planeNode;

@end

@implementation ARSCNViewViewController

- (void)viewDidLoad {
    [super viewDidLoad];


    // Do any additional setup after loading the view.
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    //1.將AR檢視新增到當前檢視
    [self.view addSubview:self.arSCNView];
    //2.開啟AR會話(此時相機開始工作)
    [self.arSession runWithConfiguration:self.arSessionConfiguration];

}

#pragma mark- 點選螢幕新增飛機
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //1.使用場景載入scn檔案(scn格式檔案是一個基於3D建模的檔案,使用3DMax軟體可以建立,這裡系統有一個預設的3D飛機)--------在右側我添加了許多3D模型,只需要替換檔名即可
    SCNScene *scene = [SCNScene sceneNamed:@"Models.scnassets/chair/chair.scn"];
    //2.獲取飛機節點(一個場景會有多個節點,此處我們只寫,飛機節點則預設是場景子節點的第一個)
    //所有的場景有且只有一個根節點,其他所有節點都是根節點的子節點
    SCNNode *shipNode = scene.rootNode.childNodes[0];

    //椅子比較大,可以可以調整Z軸的位置讓它離攝像頭遠一點,,然後再往下一點(椅子太高我們坐不上去)就可以看得全域性一點
    shipNode.position = SCNVector3Make(0, -1, -1);//x/y/z/座標相對於世界原點,也就是相機位置

    //3.將飛機節點新增到當前螢幕中
    [self.arSCNView.scene.rootNode addChildNode:shipNode];
}

#pragma mark -搭建ARKit環境


//懶載入會話追蹤配置
- (ARSessionConfiguration *)arSessionConfiguration
{
    if (_arSessionConfiguration != nil) {
        return _arSessionConfiguration;
    }

    //1.建立世界追蹤會話配置(使用ARWorldTrackingSessionConfiguration效果更加好),需要A9晶片支援
    ARWorldTrackingSessionConfiguration *configuration = [[ARWorldTrackingSessionConfiguration alloc] init];
    //2.設定追蹤方向(追蹤平面,後面會用到)
    configuration.planeDetection = ARPlaneDetectionHorizontal;
    _arSessionConfiguration = configuration;
    //3.自適應燈光(相機從暗到強光快速過渡效果會平緩一些)
    _arSessionConfiguration.lightEstimationEnabled = YES;

    return _arSessionConfiguration;

}

//懶載入拍攝會話
- (ARSession *)arSession
{
    if(_arSession != nil)
    {
        return _arSession;
    }
    //1.建立會話
    _arSession = [[ARSession alloc] init];
    //2返回會話
    return _arSession;
}

//建立AR檢視
- (ARSCNView *)arSCNView
{
    if (_arSCNView != nil) {
        return _arSCNView;
    }
    //1.建立AR檢視
    _arSCNView = [[ARSCNView alloc] initWithFrame:self.view.bounds];
    //2.設定檢視會話
    _arSCNView.session = self.arSession;
    //3.自動重新整理燈光(3D遊戲用到,此處可忽略)
    _arSCNView.automaticallyUpdatesLighting = YES;

    return _arSCNView;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end