ARKit 3 + SceneKit で People Occlusion を実現する

## 概要
- ARKit 3 の People Occlusion を使用したサンプルアプリを動かす
- Storyboard の状態を修正せず、ソースコードだけで実現する

## People Occlusion とは

ARKit 2 までは、AR コンテンツは背景の画像の上にオーバーレイ描画されるだけだった。遠くに置いた AR コンテンツの前に人がいても、ARコンテンツは隠されずに表示されてしまっていた。

ARKit 3 の People Occlusion を使うと、仮想コンテンツと人の距離を考慮して表示してくれる。具体的には、人の位置が仮想コンテンツの位置よりもカメラに近い場合には、仮想コンテンツを隠すように描画する。

[iOS 13 \- 特長 \- Apple（日本）](https://www.apple.com/jp/ios/ios-13/features/)

> ピープルオクルージョン
> ARコンテンツが人の手前や後ろに自然に現れるので、AR体験が一段と臨場感あふれるものになりました。楽しいグリーンバックのエフェクトもアプリケーションで使えます。

## People Occlusion は A12/A12X Bionic 以降を搭載していないと使えない

[iOS 13 \- 特長 \- Apple（日本）](https://www.apple.com/jp/ios/ios-13/features/)

> iPhone XR以降、11インチiPad Pro、12.9インチiPad Pro（第3世代）、iPad Air（第3世代）、iPad mini（第5世代）で利用できます。

[Augmented Reality \- ARKit 3 \- Apple Developer](https://developer.apple.com/augmented-reality/arkit/)

> People Occlusion and the use of motion capture, simultaneous front and back camera, and multiple face tracking are supported on devices with A12/A12X Bionic chips, ANE, and TrueDepth Camera.

## 今回の環境

- Xcode 11.0
- ARKit 3
- SceneKit
- Swift 5.1
- iPhone 11 Pro + iOS 13.0

## プロジェクトの作成

Xcode にてテンプレートから iOS + Argumented Reality App を選択する。

<img width="727" alt="people-occlusion-1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/145248f6-5d22-0f28-deb7-7f2a7a80d2a6.png">

以下のオプションを選択する。

- Language: Swift
- Content Technology: SceneKit
- User Interface: Storyboard

<img width="729" alt="people-occlusion-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/dc638121-adf3-8097-9a90-c3e8264dc76f.png">

プロジェクトを作成すると AppDelegate.swift と ViewController.swift という Swift のソースコードが用意される。

<img width="994" alt="people-occlusion-3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/c60c40d6-9233-cb3f-c6a8-2b2d4892cda3.png">

## ソースコード

ViewController.swift を以下のコードに置き換える。

```swift
import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set the view's delegate
        sceneView.delegate = self

        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true

        // シーンを生成
        let scene = SCNScene()

        // Set the scene to the view
        sceneView.scene = scene

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // Create a session configuration
        let configuration = ARWorldTrackingConfiguration()

        // 環境マッピングを有効にする
        configuration.environmentTexturing = .automatic

        // People Occlusion が使える端末か判定
        var message:String;
        if ARWorldTrackingConfiguration.supportsFrameSemantics(.personSegmentationWithDepth) {
            // People Occlusion を使用する
            configuration.frameSemantics = .personSegmentationWithDepth
            message = "Ok! This device supports people occulusion."
        } else {
            message = "No! This device don't support people occulusion."
        }
        print("\(message)")

        // 表示するテキストを用意
        let depth:CGFloat = 0.2 // テキストの厚さ0.2m
        let text = SCNText(string: message, extrusionDepth: depth)
        text.font = UIFont.systemFont(ofSize: 1.0)

        // テキストの色と質感を用意
        let m1 = SCNMaterial()
        m1.diffuse.contents = UIColor.red // 前面に赤色
        // 鏡面反射感を出す
        m1.lightingModel = .physicallyBased
        m1.metalness.contents = 1.0
        m1.metalness.intensity = 1.0
        m1.roughness.intensity = 0.0
        // back material
        let m2 = SCNMaterial()
        m2.diffuse.contents = UIColor.green // 背面に緑色
        m2.lightingModel = .physicallyBased
        m2.metalness.contents = 1.0
        m2.metalness.intensity = 1.0
        m2.roughness.intensity = 0.0
        // extruded sides material
        let m3 = SCNMaterial()
        m3.diffuse.contents = UIColor.blue // 側面に青色
        m3.lightingModel = .physicallyBased
        m3.metalness.contents = 1.0
        m3.metalness.intensity = 1.0
        m3.roughness.intensity = 0.0
        text.materials = [m1, m2, m3] // テキストの色と質感をセット

        // テキストノード作成して、配置する場所を決める
        let textNode = SCNNode(geometry: text)
        textNode.position = SCNVector3(-0.5, -1.5, -0.5)

        // AR空間にテキストノードを追加
        sceneView.scene.rootNode.addChildNode(textNode)

        // Run the view's session
        sceneView.session.run(configuration)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // Pause the view's session
        sceneView.session.pause()
    }
}
```

## 実機での実行結果

実機 (iPhone 11 Pro + iOS 13.0) にインストールして実行してみる。
iPhone 11 Pro は A12 Bionic 以降を搭載しているので People Occlusion が使える。

手の位置を考慮して AR コンテンツが描画されている。手が前に来ると AR コンテンツの一部が隠れる。ARKit 2 ではこれができなかった。

![people-occlusion-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/cf41c9a0-3492-1aaf-0737-de0d02cef040.jpeg)

![people-occulusion.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139365/55f746dc-fe4b-3952-86d6-571b564d47ae.gif)

## 参考資料

- [Introducing ARKit 3 \- WWDC 2019 \- Videos \- Apple Developer](https://developer.apple.com/videos/play/wwdc2019/604)
- [personSegmentationWithDepth \- ARConfiguration\.FrameSemantics \| Apple Developer Documentation](https://developer.apple.com/documentation/arkit/arconfiguration/framesemantics/3194576-personsegmentationwithdepth)