ネイティブ未経験からARKitを習得する３ヵ月

# はじめに
私はBackendエンジニアです。
ARはもとよりネイティブアプリを一から作ることは全くの未経験でした。
そんな状況でARアプリを作るには・・・という3ヵ月で得た知見を記します。

## 3ヵ月の内訳

|期間|担当範囲|スキル状況|
|:--|:--|:--|
|担当前|-|Swiftはコードリーディング程度で、3Dの知識も"右手の法則・・・？"レベル|
|1ヵ月目|ネイティブアプリエンジニアにベースを作ってもらい、なんとなくで継接いでいく|マーカー型の機能を使い、認識したマーカーの真上にオブジェクトを置くことが出来る|
|2ヵ月目|座標共有機能(ARWorldMap+MultiPeerConnectivity)を使った端末間の座標を共有したアニメーション機能|[Apple Developer Document](https://developer.apple.com/documentation/arkit)やApple提供のSampleCodeを読みながらAR特有の機能を実装出来る|
|3ヵ月目|平面検知からオブジェクト配置、別のオブジェクトを相対位置に配置し、2つのオブジェクトを相互にアニメーションさせる機能|平行移動をある程度理解して実装出来る|
|延長2週間|新規ARアプリを一から担当|ARアプリを独力で実装出来る|


# AR概略
## ARについて

 - 拡張現実（Augmented Reality）
 - 現実世界をコンピューティングで拡張する技術

最近はスマホのカメラ使ってたら大体ARだと思っています。
![](https://2.bp.blogspot.com/-DGh5Wdj5Acw/V33CmhMHZnI/AAAAAAAA8I0/O6RDhwExWhAM5zEZsV55lxC-0ms-K8_pACLcB/s180-c/game_ar_green.png)

## 主要技術
|技術|詳細|備考|
|:--|:--|:--|
|位置認識|機器のセンサーを使い、現実世界の位置を仮想世界の座標に落とし込む||
|マーカー|事前に定義したマーカーを現実世界から検出し、座標を求める|画像検出・立体検出|
|マーカーレス|現実世界の物質を検出し、座標を求める|平面検出・垂直平面検出|
|その他|-|顔認識など|

## 周辺技術

|名称|概要|
|:--|:--|
|ARToolkit|奈良先端科学技術大学院大学の加藤博一教授によって開発されたC言語ライブラリ<br>[wiki](https://ja.wikipedia.org/wiki/ARToolKit)より|
|ARKit|iOS用ライブラリ|
|ARCore|Android用ライブラリ|
|Unity|3Dゲームエンジンだが、ARKit・ARCoreをサポートしている|
|AR.js|[javascriptOSS](https://github.com/jeromeetienne/AR.js)<br>マーカー型onlyのため注意|
|その他|いろいろある|

# ARKitについて
## 用意するもの
これだけ用意出来ればARKitを使ったアプリを作れます。

 - Mac & Xcode
 - iPhone（6s以上の端末）
 - AppleID

※[ARKit対応端末はこちら](https://www.apple.com/jp/ios/augmented-reality/#footnote-1)

# ARKitを習得するための難易度別解説

## 初心者編 ～基礎知識が無くてもセンスで出来るレベル～

### ARしてみる
iOSアプリはXCodeのテンプレートプロジェクトを使うのが初めの一歩です。
テンプレートには「Augmented Reality App」があるので、それを選択しましょう
ビルドするだけで飛行機の3Dオブジェクトが目の前に登場することになります。

|TemplateProject|ビルド後|
|:--|:--|
|![](https://a-terarara.github.io/lightning-talks_arkit2/images/create_project.png)|![](https://a-terarara.github.io/lightning-talks_arkit2/images/plane_initial.jpg)

### マーカーを使ってARしてみる

1. マーカー（画像）を登録する<br>AssetsにAR ResourceGroupを追加![](https://a-terarara.github.io/lightning-talks_arkit2/images/ar_resourcegroup_add.png)<br>AR ReferenceImageを設定。physical sizeを設定。![](https://a-terarara.github.io/lightning-talks_arkit2/images/ar_reference_image.png)
1. マーカーを認識したらアクション（飛行機を出現）する

画像認識処理を実装する

```swift
override func viewWillAppear(_ animated: Bool) {
  let configuration = ARWorldTrackingConfiguration()
  let images = ARReferenceImage.referenceImages(
    inGroupNamed: "AR Resources", bundle: nil)
  configuration.detectionImages = images
  configuration.planeDetection = [.vertical, .horizontal]

  sceneView.session.run(configuration)
}
```
`ARImageTrackingConfiguration`でもよいのですが、後々現実空間の座標を使うことになるので`ARWorldTrackingConfiguration`を推奨します。
状態によってマーカーを切り替えたい場合は`inGroupNamed: `を`AR ResourceGroup`の名称に合わせます。

認識した位置に飛行機を出す

```swift
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
  if let anchor = anchor as? ARImageAnchor {
    let ship = SCNScene(
      named: "ship.scn", inDirectory: "art.scnassets"
    )!
    for node in ship.rootNode.childNodes {
      node.simdTransform = anchor.transform
      sceneView.scene.rootNode.addChildNode(node)
    }
  }
}
```
`renderer`はARセッションがマーカーまたは平面等を検出した際に実行されるメソッドです。
オブジェクトにアンカーの座標を設定することを忘れずに。

## 中級者編 ～公式ドキュメントを読めば実装出来るレベル～
### 3Dオブジェクトを作る
2種類の方法があります

1. Scene Editorを使う<br>![](https://a-terarara.github.io/lightning-talks_arkit2/images/scene_editor.png)<br>ProjectにSceneFileを追加することでSceneEditorを使って3Dオブジェクトを作れます。<br>参考：[AppleDeveloperVidelos: wwdc2015](https://developer.apple.com/videos/play/wwdc2015/606/)<br>　　　※5:00頃から。
1. ソースコードからSceneKitを使う

```swift
let box = SCNBox(
  width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
box.firstMaterial?.diffuse.contents =
  UIColor(red: 0, green: 0, blue: 1.0, alpha: 1.0)
let node = SCNNode(geometry: box)
sceneView.scene.rootNode.addChildNode(node)
```
上記はシンプルな立方体。

### 要求によって検出設定周りを使い分ける
|コンフィギュレーション|概要|
|:--|:--|
|ARWorldTrackingConfiguration|現実世界（全部）をトラッキング|
|ARImageTrackingConfiguration|2D画像だけトラッキング|
|ARObjectScanningConfiguration|3Dオブジェクトだけトラッキング|
|ARFaceTrackingConfiguration|顔だけトラッキング|
汎用性は`ARWorldTrackingConfiguration`が一番高いので、まずはこれを使うことが多いです。

### シンプルなアニメーションを付ける
SCNActionを使います

|種類|メソッド|
|:--|:--|
|移動|SCNAction.move(by: SCNVector3(1,1,1), duration: 1)<br>SCNAction.move(to: SCNVector3(1,1,1), duration: 1)|
|回転|SCNAction.rotateBy(x: 1, y: 1, z: 1), duration: 1)<br>SCNAction.rotateTo(x: 1, y: 1, z: 1), duration: 1)|
|拡大|SCNAction.scale(by: 1, duration: 1)<br>SCNAction.scale(to: 1 duration: 1)|

## 上級者編 ～3D数学を学べば実装出来るレベル～
### オブジェクトの隣にオブジェクトを置く
```swift
newNode.setWorldTransform(SCNMatrix4Translate(
  node.worldTransform,
  node.boundingBox.max.x,
  0,
  0
))
```

`SCNMatrix4Translate`：4x4行列を平行移動変換し、新しい行列を返します。
`worldTransform`：nodeのワールド座標を4x4行列で返します。
`boundingBox.max.x`：nodeの中心座標から最大のx座標までの距離を返します。

### 3Dオブジェクトを動的に変形する
```swift
let box = SCNBox(
  width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
let redMaterial = SCNMaterial()
redMaterial.diffuse.contents =
  UIColor(red:1, green:0, blue: 0, alpha: 1)
let blueMaterial = SCNMaterial()
blueMaterial.diffuse.contents =
  UIColor(red: 0, green: 0, blue: 1.0, alpha: 1.0)
box.materials = [redMaterial, blueMaterial]
```
SCNBoxのマテリアル（面）に赤と青のTextureを貼り付ける。

# おわりに
0ヵ月目の私は「ARって相当難しい技術を使うんでしょ・・・」と滅入っていましたが、ここでまとめた **[初心者編](#初心者編 ～基礎知識が無くてもセンスで出来るレベル～) さえ身に着ければ ARは作れます。**
≒ ARは簡単です。
難しいのは3Dデザイン(SceneKit)です。

# 当時夜な夜な参考にさせていただきました。感謝。
 - [Apple Developer Document](https://developer.apple.com/documentation/arkit)
 - [@shu223](https://qiita.com/shu223)
 - [@k-boy](https://qiita.com/k-boy)
 - [Apple Engine blog](http://appleengine.hatenablog.com/)