【ARKit】今日からはじめる AR プログラミング Part.2「タップした場所に画像を浮かべる」

#この記事について
前回の記事（ [今日からはじめる AR プログラミング Part.1](https://qiita.com/chino_tweet/items/cb14518f3c91d3f66c04)）では、
ARKit を使ったシンプルなアプリケーションを開発して、どのように空間トラッキングをしているかを体験した。
今回は、もう少しだけ ARKit の機能を使ってみる。

#どんな人のための記事？
- Xcode の基本的な使い方を理解している。
- Swift プログラミングの基本を理解している。
- AR (Augmented Reality) に興味がある。

#要件
- Xcode9 以降が動作する Mac
- A9 プロセッサを搭載している iOS デバイス（iOS11~）

#開発するアプリケーションの概要
- AR 空間に、3D コンテンツの画像を配置する。
- タッチジェスチャー
- SceneKit

#ARKit と SceneKit
AR は「拡張された現実」のこと。
今回の記事では、現実空間を仮想の 3D コンテンツで拡張してみる。 
iOS プログラミングで、3D コンテンツを扱う時に便利なフレームワークが `SceneKit`。
SceneKit の仮想空間では「x,y,z」の3軸、つまり「水平、垂直、奥行き」の座標系で位置情報を扱う。

#手順
##1. Xcode プロジェクトを作成
####プロジェクトの設定
####Info.plist

##2. ユーザーインターフェース
####iPhoneX 向けのデザイン
オートレイアウト
####タップを検知する
UITapGestureRecognizer を配置する
####IBOutlet 接続
arScnView

```ruby:ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var arScnView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()        
    }
        
}
```


####IBAction 接続
handleTap メソッド

```ruby:ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var arScnView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()        
    }
        
    @IBAction func handleTap(_ sender: UITapGestureRecognizer) {
    }

}
```


##3. ソースコード
####フレームワークをインポートする
今回は ARKit に加えて、3D オブジェクトを生成・配置するために SceneKit フレームワークを使用する。

```ruby:ViewController.swift
import ARKit
import SceneKit

class ViewController: UIViewController {

    @IBOutlet weak var arScnView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
        
    @IBAction func handleTap(_ sender: UITapGestureRecognizer) {
    }

}
```

####セッションとコンフィギュレーション
[前回](https://qiita.com/chino_tweet/items/cb14518f3c91d3f66c04)とほぼ同じ内容。ただし、今回はデバッグ情報は表示しない。

```ruby:ViewController.swift
import ARKit
import SceneKit

class ViewController: UIViewController {

    @IBOutlet weak var arScnView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let scene = SCNScene()
        arScnView.scene = scene
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        let configuration = ARWorldTrackingConfiguration()
        arScnView.session.run(configuration)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
        arScnView.session.pause()
    }
    
    @IBAction func handleTap(_ sender: UITapGestureRecognizer) {
    }

}
```


####完成したソースコード

```ruby:ViewController.swift
import ARKit
import SceneKit

class ViewController: UIViewController {

    @IBOutlet weak var arScnView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let scene = SCNScene()
        arScnView.scene = scene
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        let configuration = ARWorldTrackingConfiguration()
        arScnView.session.run(configuration)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
        arScnView.session.pause()
    }
    
    @IBAction func handleTap(_ sender: UITapGestureRecognizer) {
        guard let currentFrame = arScnView.session.currentFrame else { return  }
        
        let viewWidth  = arScnView.bounds.width
        let viewHeight = arScnView.bounds.height
        let imagePlane = SCNPlane(width: viewWidth/6000, height: viewHeight/6000)
        imagePlane.firstMaterial?.diffuse.contents = arScnView.snapshot()
        imagePlane.firstMaterial?.lightingModel = .constant
        
        let planeNode = SCNNode(geometry: imagePlane)
        arScnView.scene.rootNode.addChildNode(planeNode)
        
        var translation = matrix_identity_float4x4
        translation.columns.3.z = -0.1
        planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
    }

}
```


##4. ビルド
アプリが起動したら、画面をタップしてみる。
タップした瞬間にデバイス画面に映っていた画像が AR 空間に配置されれば、アプリケーションは正しく動作している。