yotiky Tech Blog

とあるエンジニアの備忘録

HoloLensでVuforiaを使う ~応用編~ (Unity&MRTK 2017世代)

前回、Vuforiaを導入する基本的な手順をまとめました。今回は応用編として、基本から少しだけ踏み出したTipsを紹介しておきます。
前回の続きになりますのでご覧になられていにいない方は以下を参照ください。

yotiky.hatenablog.com

また、Vuforiaに関してはこちらにもまとまってますのでぜひご一読ください。 github.com

目次

TL;DR;

Vuforiaは位置合わせでよく使いますが、Vuforia自体が重かったり、(RS4の場合)画像キャプチャや動画撮影などが行えないなどの不都合が生じるため、不要になったタイミングで機能をOFFにします。しかし、このまま機能をOFFにするとImageTarget配下にあるコンテンツ自体が表示されなくなってしまうため、コンテンツ自体は外に逃した上で、ImageTargetのPositionとRotaitonを同期するようにしなければなりません。また、ImageTarget配下に何もないとトラッキングができているのかどうかわからなくなってしまうので、代わりの3Dオブジェクトをおいてあげます。

f:id:yotiky:20180816032331p:plain

サンプルで使用したソースコードは下記を参照ください。

github.com

内容

白い皿

まず、コンテンツ自体をVuforiaの有効/無効に関係なく表示するため外に逃がします。
新たにModelsという空のGameObjectを作成し、ImageTarget配下にあった3Dオブジェクトをドラッグアンドドロップします。
ModelsのTransformが初期位置になっているか確認した上で行ってください。子に含めた後に親の位置やサイズを変えると、子の値も相対的に変化してしまいます。

image.png (2.7 kB)

さてこのままでは、Vuforiaがトラッキングできているのかどうかわからなくなってしまうため、代わりの3Dオブジェクトを置きます。 よく使っているのは、マーカーと同じサイズの薄い皿(板)を少しだけ浮かせて表示させる方法です。

ImageTarget配下にマーカーと同じサイズの薄い皿を作成します。

image.png (2.4 kB) image.png (7.4 kB)

image.png (82.6 kB)

青い皿

HoloLensでUnityを触り始めた人がすぐ直面するのが、3Dオブジェクトに色を付けたいだけなのにDefault-Materialが何一つ変更できなくて途方にくれることではないでしょうか。 最初の一歩として白以外の色を使ってみましょう。

Project上で新たにMaterialを作成します。

image.png (3.9 kB)

Rendering ModeをTransparentにし、Albedoで適当な色を選択します。

image.png (56.3 kB)

前項で作った薄い皿のInspectorを開いて、Materialsを展開し、作ったばかりのMaterialをドラッグ・アンド・ドロップします。

image.png (10.6 kB)

image.png (92.1 kB)

これでVuforiaがマーカーを認識すると、青い皿がオーバラップされて表示されるようになりました。 Unity のレンダリングには マテリアル、シェーダー、テクスチャ が使用されますが、ここではこれ以上踏み込まないので各自調べてみてください。

皿はマーカーと同じサイズを指定しているので現実のマーカーにピタッと重なって欲しいところですが、実際は多少ズレます。これはVuforiaを使った位置合わせで全体のズレを意味します。ただ、HoloLensは現状ではこの手法が一番現実的な実装で誤差の少ない手法になるかと思いますので、そのへんも頭の片隅においておくと良いかもしれません。

VuforiaをOFFにする

次にVufoiraがマーカーを認識したらコンテンツの位置を同期します。 今回は同期のトリガーとして青い皿をタップすることにします。

Project上で、MarkerTrackerと言う名前のスクリプトを追加します。
ほかのオブジェクトにマーカーの位置を通知する方法は、ReactiveProperty、Event(delegete)、直接受け渡しするなどなど色々ありますが、ここでは簡略化してActionを使います。
ソースコードは以下の通りです。

public class MarkerTracker : MonoBehaviour, IInputClickHandler
{
    public Action<Transform> Click { get; set; }

    public void OnInputClicked(InputClickedEventData eventData)
    {
        if(Click != null)
        {
            Click(transform);
        }
    }
}

このクラスはアタッチしたGameObjectをクリック(AirTap)すると、プロパティのCilckアクションに自分のTransformを渡して呼ぶだけのクラスです。 利用したい側でCilckアクションに処理を紐づけてあげます。

続いて、PositionAdjustmentと言う名前のスクリプトを追加します。 こちらは利用したい側になります。

public class PositionAdjustment : MonoBehaviour
{
    public MarkerTracker marker;
    public GameObject contents;

    void Start()
    {
        marker.Click = m =>
        {
            contents.transform.position = m.position;
            contents.transform.rotation = m.rotation;
            contents.SetActive(true);
            VuforiaBehaviour.Instance.enabled = false;
        };

        contents.SetActive(false);
    }
}

UnityのInspectorでMrakerTrackerと位置合わせしたいコンテンツ(3Dオブジェクト)を設定し、それぞれをひもづけて処理します。
起動時にコンテンツは非表示にし、クリックした際に位置を合わせて表示します。 また位置合わせが完了するので、このタイミングでVuforiaをOFFにします。

ハマることがあるとすると、GameObjectやVuforiaを無効化した際にアタッチされてるコードが無効化されて上手く機能しないケースでしょうか。 GameObjectの関係やコードの寄生先には注意をはらいましょう。

最後にUnity上でスクリプトをアタッチしましょう。

青い皿にMarkerTrackerを。

image.png (2.3 kB) image.png (12.8 kB)

ModelsにPositionAdjustmentを。

image.png (2.5 kB) image.png (22.0 kB)

PositionAdjutsment > Markerには青い皿、Contnetsにはコンテンツ(3Dオブジェクト)を設定します。

image.png (96.4 kB)

以上になります。
実機で動作を確認してみてください。