ARKit 3.0の何が凄いのか

<center><img width="600" alt="arkit.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/d54226dc-803c-15a9-513e-076ddc25f6e8.png"></center>

WWDC2019から少し間が開きましたが、昨年書いた記事[ARKit 2.0の何が凄いのか](https://qiita.com/k-boy/items/d935b76fed67e11b9fb2)に続いて今年もARKit3.0をまとめたいと思います。

私は2017年10月からGrafftiyというスタートアップにエンジニアとして携わり、**ARKitを使ったアプリをずっと作ってきました。**現在はフリーランスとして[YouTuber](https://www.youtube.com/channel/UCEj6hquMBUiQGunwIO1zVZA)等もしながら活動してます。

（まだbetaですので変更する可能性があります。変更を見つけ次第追記していきます。画像は基本的にWWDCの映像[introducing ARKit | WWDC](https://developer.apple.com/videos/play/wwdc2019/604/)のものです。）

# 人物オクルージョン

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/1bb39123-4c2a-27e6-6fa1-bdbceebae42f.gif" width="200">

ついにARKitにデフォルトでオクルージョンが実装されました。しかし対応デバイスはA12チップ以上を搭載している端末のみで、iPhoneでいうとiPhoneXS以降のみです。しばらくは皆が使える状態にはなりません。

基本的にARKitは[ARConfigration](https://developer.apple.com/documentation/arkit/arconfiguration)をARSessionに渡して起動するのですが、[ARWorldTrackingConfiguration](https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration)に新しい`frameSemantics`というパラメータが追加され、人物オクルージョンができるようになりました。

```swift
let configuration = ARWorldTrackingConfiguration()
configuration.frameSemantics = .personSegmentationWithDepth
session.run(configuration)
```

frameSemanticsには２種類あって

### personSegmentation

奥行きは含めずに人物の形だけをセグメント

<img width="349" alt="スクリーンショット 2019-07-26 17.56.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/49434dfa-03c9-6d47-bed6-df6f704604ed.png">

### personSegmentationWithDepth

奥行きも含めて人物の形をセグメント

<img width="323" alt="スクリーンショット 2019-07-26 17.56.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/62e3aa4f-56e3-2470-b827-57d719c3d213.png">

とそれぞれ用途に応じて使い分けることができます。もちろんDepthを取ればパフォーマンスが必要になり、電池消耗が速くなるでしょう。

Configrationだけ設定すれば、あとは物体の前を人が通ったり手をかざすと、手の部分はレンダリングしないようにしてくれます。簡単ですね。

# モーションキャプチャ

![body.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/adad85fc-f683-533f-2ca0-0c5c26081407.gif)

モーションキャプチャといえば、部屋全体に専用のカメラが設置されており、その部屋の中心部にいる人の動きを認識するイメージがありますが、現在は機械学習の発達によりスマホのカメラ画像から人間の要素を認識してリアルタイムに動きをトラッキングできるようになっています。

↓従来のモーションキャプチャ（アウトサイドイントラッキング）のイメージ
![従来のモーションキャプチャ](https://www.toshiba-clip.com/wp-content/uploads/2018/10/03.jpg)
https://www.toshiba-clip.com/detail/6164


いままでも学習済みの機械学習モデルをCoreMLでiOSアプリに取り込むことでモーションキャプチャ自体はiOSでも実現できました。しかし、ARKitにもデフォルトで実装されたことにより、3Dのバーチャルキャラクターに動きを同期させることが容易になったのです。

モーションキャプチャを使いたい場合は[ARBodyTrackingConfiguration](https://developer.apple.com/documentation/arkit/arconfiguration/framesemantics)を使います。このとき人物オクルージョンでは手動でセットしたframeSemanticsは自動で有効になっています。

```swift
let configuration = ARBodyTrackingConfiguration()
arView.session.run(configuration)
```

RealityKitでロボットの3Dモデルをロードして動かすサンプルコードは以下のように紹介されてます。（WWDCのやつから少しいじりました）

```swift
let bodyAnchor = AnchorEntity(.body)
arView.scene.addAnchor(bodyAnchor)

_ = Entity
    .loadBodyTrackedAsync(named: "character/robot")
    .sink(receiveCompletion: { completion in
        
    }, receiveValue: { (character: BodyTrackedEntity) in
        bodyAnchor.addChild(character)
    })
```

# 前面カメラと背面カメラの同時使用

昔GraffityでARビデオチャットを作っていた時に、顔を映しながら、背面カメラも使ってARができないのか？と調べてできなくて諦めたのを覚えています。

なんとARKit3.0ではA12チップ以降の端末であれば、**前面カメラと背面カメラを同時に使うことができるのです。**注意するべき点は、表示するカメラ画像は、前面か背面かどちらかだけだということです。

[ARWorldTrackingConfiguration](https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration)を使う場合は、

```swift
let configuration = ARWorldTrackingConfiguration()
if configuration.supportsUserFaceTracking {
    configuration.userFaceTrackingEnabled = true
}
session.run(configuration)
```

逆に[ARFaceTrackingConfiguration](https://developer.apple.com/documentation/arkit/arfacetrackingconfiguration)を使う場合は、

```swift
let configuration = ARFaceTrackingConfiguration()
if configuration. supportsWorldTracking {
    configuration.isWorldTrackingEnabled = true
}
session.run(configuration)
```

てな感じでフラグをつけると有効になります。**２つの違い**ですが、

ARWorldTrackingConfigurationの場合は、シーンには背面カメラの画像が表示されつつも前面カメラからARFaceAnchorを取得することができ、そのデータから目、鼻、口などのパラメータを取得することができます。

<img width="400" alt="スクリーンショット 2019-07-26 15.34.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/33a03810-7262-8b70-0285-8efb51f5da18.png">
https://developer.apple.com/videos/play/wwdc2019/604/

逆にARFaceTrackingConfigurationの場合は、前面カメラで自分の顔を映しつつ、デバイスのワールド座標系、回転角を取得できます。主な用途については僕もまだイメージできていません。

# マルチプレイ用APIの充実

<img width="805" alt="スクリーンショット 2019-07-29 16.31.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/93e2add2-0633-f654-6608-8268b9a4d990.png">


ARKit2.0からマルチプレイができるようになったと紹介されましたが、ARKit3.0からマルチプレイをするためのAPIがより充実しました。

マルチプレイというのは要は

- **複数端末で座標系を合わせる**
- **複数端末間で端末の位置やオブジェクトの位置などをリアルタイムに共有する**

ということができればいいので、マルチプレイ自体はARKit 1.0からできましたが、ARKit 2.0、ARKit 3.0になるにつれて開発者がするべき独自実装が減っていったというイメージです。

今回主に追加されたAPIは、

- [ARSession.CollaborationData](https://developer.apple.com/documentation/arkit/arsession/collaborationdata)
- [ARParticipantAnchor](https://developer.apple.com/documentation/arkit/arparticipantanchor)

です。それぞれ使い方を解説していきます。

まず、CollaborationDataやARParticipantAnchorを使うためには、[isCollaborationEnabled](https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/3152987-iscollaborationenabled)を有効にします。

```swift
let configuration = ARWorldTrackingConfiguration()
configuration.isCollaborationEnabled = true
session.run(configuration)
```

### データを送る実装

ARSessionObserver(ARSessionDelegateが準拠してる)にある以下のメソッドで、共有すべきデータが出てきた場合は、勝手に発火してくれるので、中でMultipeerConnectivityなどの通信フレームワークを用いて、別の端末に送ります。

```swift:ARSessionObserver
override func session(_ session: ARSession, didOutputCollaborationData data: ARSession.CollaborationData) {
    // dataをMultipeerConnectivityなどを使って送る
}
```
https://developer.apple.com/documentation/arkit/arsessionobserver/3152999-session

ちなみにARKitで空間共有する方法については、[ARKitで空間共有して端末間通信する方法22選](https://qiita.com/k-boy/items/e9adf3e4b64be9ed4635)という記事に以前まとめましたので見てみてください。

ちなみにこのCollaborationData方は、Data型にシリアライズする必要がなく、そのままData型として、相手に送ることができるので便利です。

### データを受ける実装

WWDCのsessionで紹介されていた例が、MultiPeerConnectivityを使ったものなので、そちらを紹介します。以下のように、Data型をCollaborationData型に変換して、arSessionに相手から受け取ったデータを反映することができます。

```swift:MCSessionDelegate
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
    let collaborationData = ARSession.CollaborationData(data)
    arSession.update(from: collaborationData)
}

```

https://developer.apple.com/documentation/multipeerconnectivity/mcsessiondelegate/1406934-session

https://developer.apple.com/documentation/arkit/arsession/3214029-update

### ARParticipantAnchor

isCollaborationEnabledがtrueになっている状態で相手が動くと、自動でARSessionDelegateのsession(_:didAdd:)メソッドで、Anchorの追加イベントが発火します。

実際にどのくらいの頻度で発火するかの検証はまだまだできていませんが、ドキュメントによると非常に高頻度で発火するみたいです。

```swift
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
    if let anchor = anchors.first as? ARParticipantAnchor {
        let transform = anchor.transform
        // transfromを使って相手の位置を反映など
    }
}
```

この辺の実装というのは、ARKit2.0時代までは、自前で実装していたものですので、かなり便利になったなと感じています。(参考： [ARKitで位置情報を共有するためのモデルをCodableで作成する](https://qiita.com/k-boy/items/b8d648a40e7e2e28b515))

# コーチングをデフォルトで用意

これも今までは、独自で作っていたやつです。Graffityの時はデザイナーのりさにAfterEffectで作ってもらって、Lottieで描画していた思い出があります。

ARKit3.0ではデフォルトで用意してくれるようになりました。（ARCoreでは前からデフォルトであったんですけどね）

![gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/03820cad-9096-6920-7996-15572a8affae.gif)

実装はとても簡単で、自動で色々出してもらいたければ、以下のようにしてaddSubViewするだけ。

```swift
let coachingOverlay = ARCoachingOverlayView()
coachingOverlay.activatesAutomatically = true
coachingOverlay.goal = .horizontalPlane
sceneView.addSubview(coachingOverlay)
```

goalを指定することで、どういうコーチングを出すかよしなにしてくれます。

ゴールの種類↓

```swift
case anyPlane
case horizontalPlane
case tracking
case verticalPlane
```

<img width="738" alt="スクリーンショット 2019-07-29 17.33.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/b97d3e69-ccb0-74d8-a247-92f869a9e43f.png">

https://developer.apple.com/documentation/arkit/arcoachingoverlayview

# 複数人の顔認識

<img width="786" alt="スクリーンショット 2019-07-29 17.38.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/1c46922a-de8a-f0d0-71bb-b6cfc408918e.png">

現状、最大３つの顔までトラッキングできるようになり、それぞれの顔にidが振られるようになりました。（[Introducing ARKit 3](https://developer.apple.com/videos/play/wwdc2019/604/)でARKit2.0から複数人の顔認識できるって言ってるけど2.0ではできないと思うので多分言い間違え？）

以下みたいな感じで`maximumNumberOfTrackedFaces`を指定できます。

```swift
let configuration = ARFaceTrackingConfiguration()
configuration.maximumNumberOfTrackedFaces = 3
session.run(configuration)
```

# 平面認識がパワーアップ

WWDCのデモによると、白い壁でも機械学習の力で壁として認識しやすくなったようです。今まで白い壁だと全く認識しなかったので期待大。

<img width="954" alt="スクリーンショット 2019-07-23 13.00.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/f8d960ed-998b-0170-daa0-44924071bf26.png">

平面は、`ARPlaneAnchor.Classification`のenumで見分けられます。（これはARKit1.0から使えるみたい。知らなかった。。。）

```swift
case wall
case floor
case ceiling
case table
case seat
```

https://developer.apple.com/documentation/arkit/arplaneanchor/classification

# RealityKitとの関係

一言で言うと、RealityKitの方が簡易的。例えばARKitがSceneKitにある機能であるパーティクルを用いたリッチな表現ができるところを、RealityKitではできなかったりするようです。

詳しくは、[RealityKit の説明と SceneKit との違いについて考える (Xcode 11.0 Beta 1) | Apple Engine](http://appleengine.hatenablog.com/entry/2019/06/07/052651)がとても詳しいので読んでみてください。

以上。随時更新します。

# 参考文献

- [ARKit公式document](https://developer.apple.com/documentation/arkit)
- [introducing ARKit | WWDC](https://developer.apple.com/videos/play/wwdc2019/604/)