【iOS】初心者が公式Sample AppをながらARKitとかMetalを理解する①

# はじめに

こんにちは、プログラミングを始めて約3年のエンジニアのkeitaMaxです。

今回はポイントクラウドのサンプルアプリと、公式を見ながらARKitとかMetalとか、ついでにSwift UIとかをふんわりと理解していこうと思います。

https://developer.apple.com/documentation/arkit/arkit_in_ios/environmental_analysis/displaying_a_point_cloud_using_scene_depth

このサンプルアプリで色々学びたいと思います。

# ダウンロードから実機にいれる

まずサイトに行ってダウンロードします。

ダウンロードが完了したら、xcodeで開いて、チームを自分のものに設定します。

これでビルドボタンを押せば、実機に入れられます。

すると、カメラが起動して自分の周りのものが点群としてスキャンされています。

なんかぐあんぐあん動いています。

# 表示するところを見てみる

`MetalViewSample.swift`どうやらここで表示する処理をしていそうです。

`MetalDepthView`この中の`body`が起動した時に表示されるところっぽいです。

じっさいに点群を表示しているところは`MetalPointCloud`ここのようです。

### デバイスが対応している機種かどうか

>デバイスがシーンの深度にアクセスするには、LiDAR スキャナーが必要です。深度視覚化ビューのbody定義では、アプリはデバイスがシーンの深度をサポートしているかどうかを確認することで、サポートされていない構成の実行を防ぎます。

```swift
if !ARWorldTrackingConfiguration.supportsFrameSemantics([.sceneDepth, .smoothedSceneDepth]) {
            Text("Unsupported Device: This app requires the LiDAR Scanner to access the scene's depth.")
        } 
```

そして、上記の部分で点群をスキャンできるデバイスかどうかを判定していて、ダメな時は文言を表示する、大丈夫だったら点群スキャンの画面を表示する処理をしています。

## ARProviderを見てみる

>データの取得と表示を分離するために、サンプル アプリは ARKit 呼び出しをそのARProviderクラスにラップします。

```
var arProvider: ARProvider = ARProvider()
```
ここでARKitの呼び出しをしていて、`ARProvider`の中で実際に点群データを取得しているらしいです。

`ARProvider`関数の中を未定見ると、`arReceiver`をの制御をしていました。

```
init()
```
ここでMetalを使用して、GPU側に処理を渡しているっぽいです。
まだ詳しくは調べていないので、今後ゆっくり調べて理解していきたいです。

## ARReceiverを見てみる

ここは実際にARSessionを開始して点群を取得している処理が書かれていました。

```swift:init()
    override init() {
        super.init()
        arSession.delegate = self
        start()
    }
```
初期化処理の時にstart()関数を読み込んでいます。

```swift:start()
    func start() {
        guard ARWorldTrackingConfiguration.supportsFrameSemantics([.sceneDepth, .smoothedSceneDepth]) else { return }
        // Enable both the `sceneDepth` and `smoothedSceneDepth` frame semantics.
        let config = ARWorldTrackingConfiguration()
        config.frameSemantics = [.sceneDepth, .smoothedSceneDepth]
        arSession.run(config)
    }
```
ここでARSessionをrunしています。

### `[.sceneDepth, .smoothedSceneDepth]`これってなんだ？と思ったので調べてみました。

公式には以下のように書かれていました。

`.sceneDepth`
>AR エクスペリエンスにおけるデバイスの背面カメラと現実世界のオブジェクトの間の距離に関するデータ。

`.smoothedSceneDepth`
>AR エクスペリエンスでよりスムーズなビジュアルを作成する、デバイスの背面カメラと現実世界のオブジェクト間の距離測定の平均。

`.sceneDepth`は撮影した物体とスマホとの距離だということはわかったのですが、
`.smoothedSceneDepth`は平均化しているため、`.sceneDepth`よりも精度が良いもの？というように捉えておけば良いような気がしました。



```swift:session
    func session(_ session: ARSession, didUpdate frame: ARFrame) {
        if(frame.sceneDepth != nil) && (frame.smoothedSceneDepth != nil) {
            arData.depthImage = frame.sceneDepth?.depthMap
            arData.depthSmoothImage = frame.smoothedSceneDepth?.depthMap
            arData.confidenceImage = frame.sceneDepth?.confidenceMap
            arData.confidenceSmoothImage = frame.smoothedSceneDepth?.confidenceMap
            arData.colorImage = frame.capturedImage
            arData.cameraIntrinsics = frame.camera.intrinsics
            arData.cameraResolution = frame.camera.imageResolution
            delegate?.onNewARData(arData: arData)
        }
    }
```
>新しくキャプチャされたカメラ画像と付随する AR 情報をデリゲートに提供します。

`didUpdate`はどうやら点群が新しく取得できた時に呼ばれるもののようです。

`frame`というものに、カメラの情報や点群の情報が記載されていそうです。

そして、`arData`に点群情報をまとめて入れているようです、

そしてまとめて入れたものを、
```swift
delegate?.onNewARData(arData: arData)
```
この処理で`ARProvider`に渡しています。

# おわりに

今回は疲れたのでここまでにします。

ARの処理は予想以上に難しくてなかなか理解が進みません。。。

初心者にはすこしレベルが高かったかなあと思うところもありますが、諦めずにみていこうと思います。

この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。（公式みて自分なりに理解しているだけなので、少しでも「ん？」と思うことがあったら教えていただけると助かります。）

最後まで読んでいただきありがとうございました！


# 参考文献

https://developer.apple.com/documentation/arkit/arkit_in_ios/environmental_analysis/displaying_a_point_cloud_using_scene_depth


# 次の記事