ARKit 1.5 で Vuforia のような画像認識を行う
ARKit 1.5 から Vuforia のような画像認識を行うことができるようになった。
壁や床などの画像を想定しているようなので、現象 Vuforia ほどの正確さはなく、
今後のアップデートで期待したい。
また、公式でサンプルが用意されているため、英語を読むのが苦にならない方は、公式の方を読んでもらった方が良いと思われる。
Recognizing Images in an AR Experience | Apple Developer Documentation
ひとまず注意点
- Beta 版であるため今後仕様が変更される可能性がある
- Beta 版の Xcode 9.3 使用するため iOS Developer Program 加入している必要がある
- 端末カメラを使用するため iOS 11.3 をインストールした実機が必要がある
- Beta 版なので認識がおかしくなることがある
- 今回は動作部分しか書かないのでエラー処理など追加する必要がある
下準備
その1:シーンファイルを消す
既存の Xcode 同様に New Project(Command+Shift+N)から「Augmented Reality App」を選択し、新しいプロジェクトを作成。
今回は AR ではテンプレートとしてつくられる Scene File を使用しないので ViewController.swift の27行目を変更
こちらから
let scene = SCNScene(named: "art.scnassets/ship.scn")!
こちらに変更
let scene = SCNScene()
Scene File は必要ないので art.scnassets も消してもらっても構わない。
その2:デバッグオプションで特徴点を描画する
この状態が表示しても現実世界のトラッキングか可能にになっているかわからないので、デバッグオプションで特徴点の表示を設定する。 以下のコードを先ほどの 「let scene = 〜」の上らへんに書く
sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
実機にビルド後、トラッキングが可能になると特徴点が黄色い点として画面に描画される。
その3:ARWorldTrackingConfiguration の平面認識設定の追加
viewWillAppear にある「let configuration = ARWorldTrackingConfiguration()」の下に、 以下のものを書き、水平と垂直の平面を認識できるようにする。
configuration.planeDetection = [.vertical, .horizontal]
ひとまず準備完了。
制作の流れ
- 認識させる画像を設定する
- ARWorldTrackingConfiguration で認識させる画像を渡す
- ARKit のデリゲート didUpdate でトラッキングが変更されるたび処理する命令を書く
- DispatchQueue 内で処理をかく
- アンカーば画像認識用か調べる
- 取得したノードにジオメトリがあるかないか調べる
- ジオメトリがない場合は、ジオメトリを設定し ARImageAnchor から大きさとマテリアルを設定する
- 位置と回転を変更する
認識させる画像を設定する
方法は以下の3つ。
- ARReferenceImage から CGImage で画像を設定する。
- ARReferenceImage から Core Video の pixel buffer で画像を設定する。
- アセットカタログに画像をドラッグ&ドロップで設定する
今回はアセットカタログから設定する。
Xcode から Assets.xcassets などのアセットを開いてAppIcon の領域で右クリックするか、その左下の「+」ボタン、またはメニューの Editor > Add Assets から「New AR Resource Group」を選択。
AR Resources というグループ名の項目ができるので選択して画像をドロップ。
デフォルトではファイル名が name になる。
ちなみに Size の値を設定しないと画像が認識されないので注意。
コードを書く
ARWorldTrackingConfiguration の設定値を追加する
まず、先ほど設定した画像を ARWorldTrackingConfiguration で設定する。
上の方で設定した「configuration.planeDetection = [.vertical, .horizontal]」の下に以下のコードを書く。
configuration.detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil)
inGroupNamed はアセットカタログで作成したグループの名前、bundle は nil にすると Main Bundle からグループ名を検索する。
また、アセットカタログで取得する場合は、外部から読み込まない限り、基本的には画像が見つからないということはないのでエラー処理は省いた。
(detectionImages は Set<ARReferenceImage>? で設定されているため、認識用の画像がなくてもアプリは落ちないので注意)
ARKit のデリゲートで処理を書く
今回は画像を上に貼り付けるだけの処理をする。
以下のコード「// MARK: - ARSCNViewDelegate」 の下あたりに書いてビルドすると画像認識が行われる。
// ARKit のデリゲート didUpdate でトラッキングが変更されるたび処理する命令を書く func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { // DispatchQueue 内で処理をかく DispatchQueue.main.async { // アンカーが画像認識用か調べる if let imageAnchor = anchor as? ARImageAnchor { // ジオメトリが設定されていなければ、ジオメトリを設定 if(node.geometry == nil){ let plane = SCNPlane() // アンカーの大きさをジオメトリに反映させる plane.width = imageAnchor.referenceImage.physicalSize.width plane.height = imageAnchor.referenceImage.physicalSize.height // 今回は book2.jpg という画像を認識した画像の上に貼り付ける plane.firstMaterial?.diffuse.contents = UIImage(named:"book2.jpg") // 設置しているノードへジオメトリを渡し描画する node.geometry = plane } // 位置の変更 node.simdTransform = imageAnchor.transform // 回転の変更 node.eulerAngles.x = 0 } } }
補足:振る舞い
多分、壁や机など固定された画像に対して画像認識をするケースを想定されており、
認識用の画像に対して複数の画像を使用できないような振る舞いになっている。
(複数できないのは確かドキュメントのどっかに書いてあった気がするが失念。違ったらすみません)
Beta なので今後改善されるだろう。
また、今回は didUpdate のデリゲートを使用しているがアンカーの振る舞いが安定しないため didAdd でも構わないと思う。
公式サンプルでは didAdd の方を使用している。
補足:非同期
「DispatchQueue.main.async」で非同期の処理を設定しているがメインキューでやる必要性はないので、普通のシリアルキューでも問題ないと思われる。
(新しい Apple のサンプルではメインキューで使用していない)
補足:アニメーションするジオメトリを置く際の注意
float 4x4 の行列で返ってくるため、回転などのアニメーションするノードにそのまま渡すと動きがおかしくなる可能性がある。
その場合は空のノードにアニメーションをさせて、それを addChild した方が良いと思われる。
まとめ
美術館などで作品の説明をしたり、ゲームなどでアイテムを表示するなど、現実世界を AR で描き返る参照となり、様々なものに利用できる。
また、画像を初期位置とし他の端末と場所の共有や各端末の振る舞いの共有などもできるかもしれない。
リアルと仮想空間をミックスした空間共有というのもの面白いと思われる。