實踐解析:利用ARKit實現直播場景虛擬化(上篇)

編者按:AR遊戲很常見,但在直播畫面中出現AR屏幕,在其中再嵌套直播畫面的玩法你試過麼?咱們將經過兩篇文章由淺入深,帶你一塊兒看看AR能與音視頻直播碰撞出怎樣的火花。node

本篇咱們將從ARKit開發原理,到demo代碼,帶你們一塊兒實現一個AR應用。數組

下篇咱們將圍繞音視頻信息採集,到場景渲染,與你們分享如何實現AR視頻會議的demo實例。歡迎訪問 RTC 開發者社區,與更多開發者交流經驗,參與更多技術活動。bash

今年7月Apple推出了AR工具ARKit,着實閃着了你們的眼睛。從目前的評測能夠知道 ARKit已經很是成熟,徹底能夠進行商用了。session

在iOS中,加強現實由ARKit和渲染兩部分組成。ARKit主要負責AR計算,它將ARCamera捕獲的視頻幀看成背景,使用視覺慣性測距(VIO)來精確跟蹤周圍的世界,進行座標轉換,場景搭建及平面的捕獲;而後,經過 SceneKit(3D)/SpritKit(2D) 或 Metal 庫進行渲染,使虛擬物體與真實世界相接合,達到加強現實的目的。ide

今天咱們就來詳細的瞭解一下 ARKit,看看 Apple 爲咱們提供了怎樣強大的工具,可讓咱們迅速的構建一個AR應用程序。工具

在講解咱們的 AR 程序以前,咱們先要了解幾個ARKit的基本概念。只有這幾個基本概念瞭解清楚以後,咱們才能清楚的知道如何去寫一個AR程序。post

幾個重要概念

  • 空間定位與方向追蹤 這個是經過 ARCamera 獲取到視頻幀,再通過 VIO 計算出來的。
  • 場景理解,平臺檢測 ,點擊檢測,光照檢測 這是經過ARSession管理的內部模塊計算得出的。
  • 渲染層 能夠經過 SceneKit/SpritKit 或 Metal/OpenGL 進行渲染。今天主要介紹 SceneKit進行渲染。

什麼是特徵點

AR 的目標是往真實世界中的特定點插入虛擬內容,而且在真實世界中移動時還能對此虛擬內容保持追蹤。優化

ARKit 從視頻幀中得到某張圖片的特徵後,就能夠從多個幀中追蹤這些特徵。隨着用戶在真實世界中的移動,就能夠利用相應的特徵點來估算 3D 姿態信息。用戶移動地越多,就會得到越多的特徵,並優化這些估算的 3D 姿態信息。ui

有沒有可能檢測不出特徵點的狀況呢?固然有,可能檢測不出特徵點的狀況以下:atom

  • 光線差 沒有足夠的光或光線過強的鏡面反光。嘗試避免這些光線差的環境。
  • 缺乏紋理 若是攝像頭指向一面白牆,那也無法得到特徵,ARKit 也去沒法找到並追蹤用戶。嘗試避免看向純色、反光表面等地方。
  • 快速移動 一般狀況下檢測和估算 3D 姿態只會藉助圖片,若是攝像頭移動太快圖片就會糊,從而致使追蹤失敗。但 ARKit 會利用視覺慣性里程計,綜合圖片信息和設備運動傳感器來估計用戶轉向的位置。所以 ARKit 在追蹤方面很是強大。

什麼是平面檢測

ARKit 的平面檢測用於檢測出現實世界的水平面,也就是在 3D 空間中,Y值爲0的一個區域。平面檢測是一個動態的過程,當攝像機不斷移動時,檢測到的平面也會不斷的變化。此外,隨着平面的動態檢測,不一樣平面也可能會合併爲一個新的平面。

只有檢測真實世界有水平面以後,才能找到錨定點,並將虛擬物體放到這個錨定點上。

什麼是點擊檢測

除了平臺檢測外,還有點擊檢測。顧名思意,就是當用戶點擊屏幕時,ARKit 將點擊屏幕的2D空間位置轉換爲ARKit 經過 ARCamera 捕獲到的視頻幀的 3D 空間位置。並在這個位置檢測是否有平面。

什麼是世界追蹤

世界追蹤都追蹤什麼呢?ARKit 會追蹤如下幾個信息:

  • 追蹤設備的位置以及旋轉,這兩個信息均是相對於設備起始時的信息。
  • 追蹤物理距離(以「米」爲單位),例如 ARKit 檢測到一個平面,咱們但願知道這個平面有多大。
  • 追蹤咱們手動添加的但願追蹤的點,例如咱們手動添加的一個虛擬物體

ARKit 使用視覺慣性測距技術,對攝像頭採集到的圖像序列進行計算機視覺分析,而且與設備的運動傳感器信息相結合。ARKit 會識別出每一幀圖像中的特徵點,而且根據特徵點在連續的圖像幀之間的位置變化,而後與運動傳感器提供的信息進行比較,最終獲得高精度的設備位置和偏轉信息。

除了上面這幾個概念外,咱們還須要知道ARKit提供的一些基本知識。

ARSession

ARSession 是 ARkit 的核心。它是鏈接ARCamera與ARACNView之間的橋樑。像捕獲視頻,與 CoreMotion 的數據整合,場景的理解,平面檢測等等都須要 ARSession 來協調各模塊進行協同處理。

另外,ARSession 有兩種獲取 ARFrame 的方法:

  • push 實時不斷的獲取相機位置,由ARSession主動告知用戶。經過實現ARSession的代理來獲取。

    (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame
    複製代碼
  • pull 用戶想要時,主動去獲取。經過 ARSession的屬性currentFrame來獲取。

ARConfiguration

該類的做用是設置一些 ARSession 相關的配置,如是否使用平面檢測就是經過這個參數來設置的。

ARSCNView

ARSCNView 繼承自 SceneKit 中的 SCNView。ARSCNView是一個很是複雜的類,它不只擁有SCNView的功能,並且它還管理着 ARSession。以下圖所示:

SceneKit 的主要做用是將虛擬物體展現在3D場景中。每一個虛擬物體均可以用 SCNNode 來表明,SCNNode 在 SCNScene 中展示,而無數SCNScene 組成 3D 世界。

它有幾個重要的方法須要特別強調一下:

  • hitTest 方法

    - (NSArray<ARHitTestResult *> *)hitTest:(CGPoint)point types:(ARHitTestResultType)types;
    複製代碼

    point:2D座標點(手機屏幕某一點);

    ARHitTestResultType:捕捉類型是 點 仍是 面;

    NSArray<ARHitTestResult *> *:追蹤結果數組。數組的結果排序是由近到遠。

    根據2D座標點搜索3D模型位置。當咱們在手機屏幕點擊某一個點的時候,能夠捕捉到這一個點所在的3D模型的位置。爲何返回值是一個數組呢?這是由於手機屏幕一個是長方形的二維空間,而相機捕捉到的是一個由這個二維空間映射出去的長方體。咱們點擊屏幕一個點,能夠理解爲在這個長方體的邊緣射出一條線,這一條線上可能會有多個3D物體模型。

  • renderer 方法

    (void)renderer:(id<SCNSceneRenderer>)renderer updateAtTime:(NSTimeInterval)time
    複製代碼

    它是 ARSCNViewDelegate 中的回調方法,每次 3D 引擎要渲染新的視頻幀時都會調用該方法。

  • 重載 renderer 方法

    - (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
    複製代碼

    它是 ARSCNViewDelegate 中的回調方法,每次 ARKit 檢測到了平面時都會調用此方法。咱們能夠經過這個代理方法得知咱們添加一個虛擬物體到AR場景下的錨點(AR現實世界中的座標)

SCNNode

SCNNode表明一個虛擬物體。經過 SCNNode 能夠對虛擬物體進行變換和旋轉,還能夠作幾何變換,光照等操做。

SCNScene

在ARKit中它表明一個場景。SCNScene 包括背景 和 虛似物體。其中背景能夠是從 ARCamera捕獲的視頻幀。而虛擬物體由 rootNode 存儲,它就是前面介紹的 SCNNode。

ARAnchor

包含真實世界位置和方向的信息。經過它能夠輕鬆地將虛擬物體添加,更新或從會話中刪除。

ARCamera

ARCamera 用於捕捉視頻流。通常咱們無需去建立一個ARCamera,由於在初始化 AR 時,它就幫咱們將ARCamera建立好了。另外,咱們通常也不直接使用 ARCamera 的 API,默認都是設置好的。

ARFrame

攝像頭視頻幀的包裝類。從 ARCamera 中獲取的每一幅視頻幀都被封裝成 ARFrame。它包含位置追蹤信息、環境參數、視頻幀。重點是它包含了蘋果檢測的特徵點,經過rawFeaturePoints能夠獲取,不過只是特徵的位置,具體的特徵向量並無開放。

SCNMaterial

使用 SCNMaterial 能夠對虛擬物體 SCNNode 進行貼圖。

AR 任意門的實現

所謂任意門就是在真實環境中虛擬一扇門,當走進這扇門後,能夠看到另一個世界。

實現

初始化ARKit

- (void)viewDidLoad {
    [super viewDidLoad];

    // Set the view's delegate self.sceneView.delegate = self; // Show statistics such as fps and timing information self.sceneView.showsStatistics = YES; // Create a new scene SCNScene *scene = [SCNScene new]; // Set the scene to the view self.sceneView.scene = scene; //Grid to identify plane detected by ARKit _gridMaterial = [SCNMaterial material]; _gridMaterial.diffuse.contents = [UIImage imageNamed:@"art.scnassets/grid.png"]; //when plane scaling large, we wanna grid cover it over and over _gridMaterial.diffuse.wrapS = SCNWrapModeRepeat; _gridMaterial.diffuse.wrapT = SCNWrapModeRepeat; _planes = [NSMutableDictionary dictionary]; //tap gesture UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(placeTransDimenRoom:)]; [self.sceneView addGestureRecognizer:tap]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Create a session configuration ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new]; configuration.planeDetection = ARPlaneDetectionHorizontal; // Run the view's session
    [self.sceneView.session runWithConfiguration:configuration];
    
}

複製代碼

處理ARKit檢測到的平面

用於提示能夠用於交互的平面,後期模擬物理世界也要用到。

- (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
    if ([anchor isKindOfClass:[ARPlaneAnchor class]] && !_stopDetectPlanes){
        NSLog(@"detected plane");
        [self addPlanesWithAnchor:(ARPlaneAnchor*)anchor forNode:node];
        [self postInfomation:@"touch ground to place room"];
    }
}
- (void)renderer:(id<SCNSceneRenderer>)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
    if ([anchor isKindOfClass:[ARPlaneAnchor class]]){
        NSLog(@"updated plane");
        [self updatePlanesForAnchor:(ARPlaneAnchor*)anchor];
    }
}
- (void)renderer:(id<SCNSceneRenderer>)renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
    if ([anchor isKindOfClass:[ARPlaneAnchor class]]){
        NSLog(@"removed plane");
        [self removePlaneForAnchor:(ARPlaneAnchor*)anchor];
    }
}
複製代碼

放置transDimenRoom

對於隱藏空間,抽象成兩個類來表達:transDimenRoom,transDimenStruct。

後者用於提供一些平板等基礎結構,前者將這些結構拼成一個房間,留一個門框出來讓用戶可以看見裏面。

當須要放置任意門時,就用+transDimenRoomAtPosition:方法建立一個transDimenRoom,當用戶走進去時,用 -hideWalls: 隱藏四周的牆壁,切換成全景背景。

@interface transDimenRoom : SCNNode
@property (nonatomic, strong) SCNNode *walls;

+(instancetype)transDimenRoomAtPosition:(SCNVector3)position;
//TODO:check  if user in room
-(BOOL)checkIfInRoom:(SCNVector3)position;

-(void)hideWalls:(BOOL)hidden;
@end

複製代碼

檢測到用戶走進房間

目前爲了簡單起見,是判斷用戶與房間中心的距離,當距離小於1時,就認爲用戶進入了房間。這裏的邏輯之後會收歸到transDimenRoom中。

- (void)renderer:(id<SCNSceneRenderer>)renderer updateAtTime:(NSTimeInterval)time{
    if (_room.presentationNode) {
        
        SCNVector3 position = self.sceneView.pointOfView.presentationNode.worldPosition;
        
        SCNVector3 roomCenter = _room.walls.worldPosition;
        
        CGFloat distance = GLKVector3Length(GLKVector3Make(position.x - roomCenter.x, 0, position.z - roomCenter.z));
        
        if (distance < 1){
            NSLog(@"In room");
            [self handleUserInRoom:YES];
            return;
        }
        
        [self handleUserInRoom:NO];
        
    }
}
複製代碼

小結

今天首先向你們介紹了一下 ARKit 的基本知識,而後經過任意門這個實例告訴了你們如何寫一個ARKit程序。這個 任意門 能夠應用在不少場景中,你們能夠經過這個實例進行擴展,充份發揮本身的想像力。

其實本節最最關鍵的是讓你們知道 ARKit中的那些基本概念。ARSession是它的核心,它協調內部模塊進行場景的各類計算。而 ARSCNView 只是渲染技術中的一種,咱們完成能夠經過 OpenGL/Metal 來替換掉它。

在下篇,咱們將會介紹,如何講ARkit應用於視頻直播。


相關閱讀:AR實踐:基於ARKit實現電影中的全息視頻會議

相關文章
相關標籤/搜索