ARKitで動き出すポスターを作る

ポスターやカードをカメラで写すとARが浮かび上がってくるタイプのARが流行っています。手軽でできて分かりやすいARです。この機能をARKitで実装する方法を紹介していきます。

以下みたいなイメージ。

![gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/4a451af3-6ee2-fedc-5049-14ab92440287.gif)
https://www.youtube.com/watch?v=mg-RDMab6VQ

[[Swift/ARKit2] 動くポスターを1時間でつくろう](https://qiita.com/IZUMIRU/items/3c36f7730aff5e6305c5)という記事の実装部分の解説です。

## ポスター画像と再生用の動画を用意する

　画像マーカーとなるポスター画像ファイルと、マーカーを読み取った際に再生する動画ファイルを用意しましょう。それぞれjpgファイルとmp4ファイルだと間違いないと思います。また、2Dのポスターが突然動き出すように表現するためには、それぞれ縦横比を統一した方が良いでしょう。

## ポスター画像をARImageResourceに登録する

　[**ARKitで作るAR名刺**](https://qiita.com/k-boy/items/d5c7e0f4174da4b8d5f0)と同じ容量で、ポスターが画像をARImageResourceに登録しましょう。

## ポスター動画をプロジェクトに追加する

　ポスター動画（例ではmovie.mp4というファイル）をXcodeのプロジェクトに対してドラッグアンドドロップします。

## 画像を読み取ったら反応するようにする

まずはsceneViewをARSCNViewDelegateに準拠させます。また、AR Resourcesの画像をtrackingImagesとしてARImageTrackingConfigurationにセットしときます。

```swift
override func viewDidLoad() {
    super.viewDidLoad()
        
    sceneView.delegate = self
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    let configuration = ARImageTrackingConfiguration()
    
    if let trackedImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: Bundle.main) {
        configuration.trackingImages = trackedImages
    }
    sceneView.session.run(configuration)
}
```

　以下のように`renderer(didAdd node:)`のメソッドを書き、ARImageAnchorの発見を検知します。

```swift
extension ViewController: ARSCNViewDelegate {
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let imageAnchor = anchor as? ARImageAnchor else {
            return
        }
    }
}
```

## SKVideoNodeとSKSceneで動画のシーンを作成する

　次にARKit（正確にはSceneKit）で動画を再生する方法を解説します。動画自体は2Dなので、今回SpriteKitを使って動画を再生します。SpriteKitで作られたSKSceneをSceneKitで再生していきます。

　予め用意してXcodeにドラッグアンドドロップしていたmovie.mp4を呼び出して動画を再生します。

```swift
guard let imageAnchor = anchor as? ARImageAnchor else {
    return
}
guard let fileUrlString = Bundle.main.path(forResource: "movie", ofType: "mp4") else {
    return
}
let videoItem = AVPlayerItem(url: URL(fileURLWithPath: fileUrlString))
let player = AVPlayer(playerItem: videoItem)
player.play()
```

次に、SKSceneを作成し、その中にSKVideoNodeを追加します。

```swift
let size = CGSize(width: 480, height: 360)
let videoScene = SKScene(size: size)

let videoNode = SKVideoNode(avPlayer: player)
videoNode.position = CGPoint(x: size.width / 2, y: size.height / 2)
videoNode.yScale = -1.0

videoScene.addChild(videoNode)
```

## SCNPlaneのマテリアルとして動画のシーンをセットする

　最後にポスターと同じサイズの平面のジオメトリを持つSCNNodeを作成し、そのマテリアルとして、動画を再生するSKSceneを渡すことで、動画を再生する平面のノードを作ることができます。最後にその平面をImageAnchorの位置にあるnodeに追加して完成です。

```swift
let plane = SCNPlane(
    width: imageAnchor.referenceImage.physicalSize.width,
    height: imageAnchor.referenceImage.physicalSize.height
)
plane.firstMaterial?.diffuse.contents = videoScene

let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = -Float.pi / 2
node.addChildNode(planeNode)
```

画像検知後の一連のコードは以下です。

```swift
extension ViewController: ARSCNViewDelegate {
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let imageAnchor = anchor as? ARImageAnchor,
            let fileUrlString = Bundle.main.path(forResource: "black", ofType: "mp4") else {
                return
        }
        let videoItem = AVPlayerItem(url: URL(fileURLWithPath: fileUrlString))
        let player = AVPlayer(playerItem: videoItem)
        player.play()
        
        let size = CGSize(width: 480, height: 360)
        let videoScene = SKScene(size: size)
        
        let videoNode = SKVideoNode(avPlayer: player)
        videoNode.position = CGPoint(x: size.width / 2, y: size.height / 2)
        videoNode.yScale = -1.0
        
        videoScene.addChild(videoNode)
        
        let plane = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width,
                             height: imageAnchor.referenceImage.physicalSize.height)
        plane.firstMaterial?.diffuse.contents = videoScene
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles.x = -Float.pi / 2
        node.addChildNode(planeNode)
    }
}
```

以上です。特定のポスター画像を発見すると、その位置に動画を再生することができました。

![gif](https://media.giphy.com/media/Xth62sA34DF2houlI4/giphy.gif)

（動画はイメージ [Using ARKit To Display Video In Augmented Reality](https://medium.com/quick-code/using-arkit-to-display-video-in-augmented-reality-3a2e4c1418ad)から引用）

# 関連記事
https://qiita.com/IZUMIRU/items/3c36f7730aff5e6305c5
https://medium.com/quick-code/using-arkit-to-display-video-in-augmented-reality-3a2e4c1418ad