SmartAR for Unityをちょっとディープに使ってみる

# はじめに
ソニー製、サイバネット社提供のSmartAR for Unityを使用するにあたり、「これってどうするの？」と引っかかりそうなポイントをまとめてみました。

プラクティカルな内容になるので、相変わらず正確性に欠ける表現や、私個人の主観が含まれています。あらかじめご了承ください。

# HFG/VFGを使う
HFGとVFGは、完全マーカーレスでARを実現させる（周囲環境の特徴点のみで空間を構築する）モードですが、実際にInspector上でどう設定すると使えるかというと、以下のようにすると有効になります。
![hfgSettings.png](https://qiita-image-store.s3.amazonaws.com/0/111394/222a8835-5349-6910-b0eb-ff3ca0cef903.png)

- Recognition ModeをRECOGNITION_MODE_SCENE_MAPPINGにする
- Sccene Mapping Init Modeを\*\_HFG、または\*\_VFGにする
- TargetsのSizeは1
- Elementは**Idのみ設定し、File Nameは空欄**にしておく

あとはSmartAR Targetをシーン配置して、TargetEffectorのIdを上記Targetsのものと一致させれば、HFG/VFG認識時にTargetEffectorの子オブジェクトが重畳表示されます。

前の記事でも書きましたが、ARオブジェクトの物理サイズはコントロールしきれないのでご注意ください。

# 複数ターゲットに対応する その1
**Target Trackingで、複数ターゲットを同時に認識する方法**です。
同時に２つ以上のマーカー上にARオブジェクトを表示します。いわゆる「複数マーカーの同時認識」ですが、最大の注意点としては、**smartar_moving_target.unitypackageを使用すること**です。
内部的な話ですが、一意にAR空間を定義させるため、どこかに基準点を設ける必要があります。複数ターゲットの同時認識では、ターゲットの位置と数が不定なため、必然的にカメラを原点に固定することとなります。

同時認識数は、Inspector上からSmartAR ControllerのMax Targets Per Frameで忘れずに設定してください。

ドキュメントにも記載されていますが、moving_targetでは、TargetEffectorの子オブジェクト全体に対して常に姿勢の補正が適用されるので、ARオブジェクトが増えれば増えるほど、負荷も増大します。

複数ターゲットのトラッキング精度について、過度な期待は厳禁です。単一ターゲットのトラッキング精度と2つ以上の精度では、それはもうびっくりするくらいの差があります。

尚、異なる辞書のターゲットを複数認識することは可能ですが、**同じターゲットを複数認識することはできません**。

// 一応追記すると、PS VitaとかPS4などネイティブで叩ける環境では、認識したターゲット位置を自前でマスクした画像を更に食わせて～とやればできないことはないですが、いずれにせよ結構な処理です

# 複数ターゲットに対応する その2
**Scene Mappingで、複数ターゲットのいずれかを認識する方法**です。

具体例を挙げると、[デジアイAR](http://jp.rohto.com/digieye/ar/)の限定パッケージなど、同じシーン内で複数のターゲットを判別する必要がある場合などに使います。

これは単純にSmartAR Controller内のTargetsを増やし、それに対応するTargetEffectorも増やすだけです。
![multiRecog.png](https://qiita-image-store.s3.amazonaws.com/0/111394/310ab7fe-9b17-9f97-7d52-d7941bc7182e.png)

SmartARはDetectorが重かった覚えがあったのですが、Xperia Z5 Compactで試してみたところ、32個のターゲット認識でも、特に負荷を感じることなく実現できました。

注意点としては、**一度認識されると、その他のターゲットは認識しなくなる**ことです。そのため、アプリケーションに応じてシーンの再読み込みボタンをつけるなど、UI側でフォローしてください。

ちなみに、Max Targets Per Frameを増やしても同時認識はできません。ご注意ください。

# カメラプレビューの解像度を上げる
SmartARを使用した際のカメラ解像度、デフォルトでVGAです。罠です。

![cameraRes.png](https://qiita-image-store.s3.amazonaws.com/0/111394/97e99e6b-840a-983d-3a00-4c8ea4950966.png)
画像のように設定してあげると、カメラプレビューがHDになります。端末が対応していない解像度を指定すると低解像度に合わせられます。

*Search Policyの補足*
認識器に画像ピラミッドをどこから食わせるか？という設定らしいです。（画像ピラミッドとかの説明は割愛します）
720pくらいの解像度があれば、結構ダウンサンプルされたところから食わせても大丈夫だろう、ということで、ここではFASTに設定しました。

# Targetの子を消す
演出の都合などで、認識中にARオブジェクトを消す方法です。
普通であればGetComponent\<Renderer\>().Enabled = false;で消すところですが、**認識中はTargetEffectorが常にRendererをtrueにします**。

そこで、transform.LocalScale = Vector3.zero;など、別の方法で表示/非表示を実現する必要があります。

# LandmarkEffectorにご注意
認識された特徴点に自前のエフェクトを出したい！というときに使うことを想定したであろうLandmarkEffectorですが、**そのままでは使い物になりません**。

あんまりディスリスペクトしたくはないのですが、以下がその理由です。

- **AwakeでGameObjectを2560個Instantiateする**
もうこの時点でダメなのですが、Landmark/Node最大数分（1024+1024+512）、あらかじめGameObjectを用意してSetActiveで管理するという設計になっているようです。


- **Stateを表すためにSetColorしている**
大量に生成されたGameObjectに対して、更にマテリアルアクセスをします。ものっすごい勢いでDrawcallが増えていきます。


- **なぜかInstantiateしたGameObjectのスケールを20倍にしている**
どう見てもテストコードやん？

幸い、C#で書かれているので自分で改造してあげれば使えないことはないはずですが、ここまでくるとイチから作った方が早いと思います。
# ターゲットを縦に認識したかったんだ
ARではマーカーを机や床に置いている絵を見ることが多いですが、壁に貼られたポスターを認識するなど、縦に置かれたターゲットを認識する場合もあります。
結論から言うと、**床置きしか想定されていないようです**。

シーンで無理やり90°傾けて配置する、TargetEffectorの子に空のGameObjectを一枚噛ませてスクリプトから回転させるなど、若干の工夫と苦痛が必要です。