次の方法で共有


チュートリアル:インターフェイスとカスタム モデル

このチュートリアルでは、以下の内容を学習します。

  • プロジェクトに Mixed Reality Toolkit を追加する
  • モデルの状態を管理する
  • モデルを取り込むために Azure Blob Storage を構成する
  • レンダリングするモデルをアップロードして処理する

前提条件

Mixed Reality Toolkit (MRTK) の概要

Mixed Reality Toolkit (MRTK) は、Mixed Reality エクスペリエンスを構築するためのクロスプラットフォームのツールキットです。 ここでは、その操作と視覚化機能に MRTK 2.8.3 を使用します。

MRTK をインポートするための公式ガイドには、実行する必要のない手順がいくつか含まれています。 次の 3 つの手順のみ必要です。

  • Mixed Reality Feature Tool (Import MRTK) 経由で 'Mixed Reality Toolkit/Mixed Reality Toolkit Foundation' バージョン 2.8.3 をプロジェクトにインポートします。
  • MRTK の構成ウィザードを実行します (MRTK の構成)。
  • 現在のシーンに MRTK を追加します (シーンに追加)。 チュートリアルで推奨されるプロファイルの代わりに、ここで ARRMixedRealityToolkitConfigurationProfile を使用します。

このチュートリアルで使用するアセットをインポートする

この章からは、取り上げられる素材の多くに基本的な Model-View-Controller パターンを実装します。 このパターンの Model 部分は、Azure Remote Rendering 固有のコードと、Azure Remote Rendering に関連した状態管理です。 パターンの View 部分と Controller 部分は、MRTK アセットといくつかのカスタム スクリプトを使用して実装します。 このチュートリアルの Model は、ここで実装する View-Controller がなくても使用できます。 このように分離されているので、このチュートリアルにあるコードを独自のアプリケーションに容易に統合することができます。このアプリケーションは、設計パターンの View-Controller 部分を引き継ぐことになります。

MRTK の導入により、インタラクションとビジュアル フィードバックをサポートするためにプロジェクトに追加できるようになった複数のスクリプト、プレハブ、アセットが存在します。 これらのアセット (チュートリアル アセットといいます) は、Azure Remote Rendering GitHub の "\Unity\TutorialAssets\TutorialAssets.unitypackage" にある Unity アセット パッケージにバンドルされています。

  1. Git リポジトリ Azure Remote Rendering をクローンまたはダウンロードします。ダウンロードした場合は、zip を既知の場所に展開します。
  2. Unity プロジェクトで、[Assets] (アセット) -> [Import Package] (パッケージのインポート) -> [Custom Package] (カスタム パッケージ) を選択します。
  3. エクスプローラーで、Azure Remote Rendering リポジトリをクローンまたは解凍したディレクトリに移動し、Unity -> TutorialAssets -> TutorialAssets.unitypackage にある .unitypackage を選択します。
  4. [インポート] ボタンを選択して、パッケージの内容をプロジェクトにインポートします。
  5. Unity エディターで、上部のメニュー バーから [Mixed Reality Toolkit] -> [Utilities] (ユーティリティ) -> [Upgrade MRTK Standard Shader for Lightweight Render Pipeline] (MRTK Standard Shader を Lightweight Render Pipeline 用にアップグレード) を選択し、画面の指示に従ってシェーダーをアップグレードします。

MRTK とチュートリアル アセットが設定されていることをダブルチェックすると、正しいプロファイルが選択されます。

  1. シーン階層の MixedRealityToolkit GameObject を選択します。
  2. インスペクターの MixedRealityToolkit コンポーネントで、構成プロファイルを ARRMixedRealityToolkitConfigurationProfile に切り替えます。
  3. Ctrl + S キーを押して、変更内容を保存します。

この手順で、主に既定の HoloLens 2 プロファイルで MRTK が構成されます。 提供されるプロファイルは、あらかじめ次のように構成されています。

  • プロファイラーをオフにする。オンとオフを切り替えるには 9 を押すか、デバイスで "Show/Hide Profiler (プロファイラーを表示/非表示にしてください)" と言います。
  • 視線カーソルをオフにする。
  • Unity のマウス クリックを有効する。シミュレートされた手ではなくマウスで、MRTK の UI 要素をクリックできるようになります。

アプリ メニューを追加する

このチュートリアルのほとんどのビュー コントローラーは、具象クラスではなく抽象基本クラスに対して動作します。 このパターンにより、柔軟性が向上し、ビュー コントローラーが与えられるだけでなく、自ら Azure Remote Rendering のコードを習得できます。 簡潔にするために、RemoteRenderingCoordinator クラスには抽象クラスは用意されておらず、そのビュー コントローラーは直接、具象クラスに対して動作します。

これで、プレハブの AppMenu をシーンに追加し、現在のセッション状態のビジュアル フィードバックを得ることができます。 AppMenu には、アプリケーションに ARR への接続を承認する際にユーザーが使用するモーダル パネルも表示されます。

  1. Assets/RemoteRenderingTutorial/Prefabs/AppMenuAppMenu プレハブを見つけます。

  2. AppMenu プレハブをシーンにドラッグします。

  3. TMP Importer のダイアログが表示された場合は、プロンプトに従って TMP Essentials をインポートします。 サンプルや追加パッケージは必要ないため、その後はインポーターのダイアログを閉じてください。

  4. AppMenu は、セッションへの接続に同意するためのモーダルに自動的に接続して表示するよう構成されているため、前に設定しておいたバイパスは削除してかまいません。 RemoteRenderingCoordinator GameObject で、 [On Requesting Authorization](承認要求時) イベントの [-] ボタンをクリックして、以前に実装した承認のバイパスを削除してください。

    バイパスを削除します

  5. Unity エディターの [Play](再生) を押して、ビュー コントローラーをテストします。

  6. エディターで MRTK が構成されたら、WASD キーを使用してビューの位置を変更したり、マウスの右ボタンを押しながらマウスを移動することでビューの方向を変更したりすることができます。 シーン内を少し動き回ってみて、コントロールの感触を確かめてください。

  7. デバイスで、手のひらを上げることで、AppMenu を呼び出すことができます。Unity エディターでは、ホットキーの "M" を使用します。

  8. メニューが見えなくなった場合は、"M" キーを押してメニューを呼び出します。 操作しやすいよう、このメニューはカメラの近くに配置されます。

  9. AppMenu は、承認用の UI 要素を AppMenu の右側に表示します。 今後は、この UI 要素を使用して、リモート レンダリング セッションを管理するアプリを承認する必要があります。

    承認の UI

  10. Unity の再生を停止して、チュートリアルを続行します。

モデルの状態を管理する

状態の追跡、イベントへの応答、イベントの生成、構成を行うための RemoteRenderedModel という新しいスクリプトを実装する必要があります。 基本的に、RemoteRenderedModel はモデル データのリモート パスを modelPath に格納します。 RemoteRenderingCoordinator で状態の変化をリッスンし、定義されているモデルを自動的に読み込む (またはアンロードする) 必要があるかどうかを判断します。 RemoteRenderedModel がアタッチされている GameObject が、リモート コンテンツのローカルにおける親になります。

RemoteRenderedModel スクリプトには、チュートリアル アセットにある BaseRemoteRenderedModel が実装されていることに注目してください。 この接続によって、リモート モデル ビュー コントローラーをスクリプトにバインドすることができます。

  1. RemoteRenderingCoordinator と同じフォルダーに、RemoteRenderedModel という名前の新しいスクリプトを作成します。 内容全体を次のコードに置き換えます。

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License. See LICENSE in the project root for license information.
    
    using Microsoft.Azure.RemoteRendering;
    using Microsoft.Azure.RemoteRendering.Unity;
    using System;
    using UnityEngine;
    using UnityEngine.Events;
    
    public class RemoteRenderedModel : BaseRemoteRenderedModel
    {
        public bool AutomaticallyLoad = true;
    
        private ModelState currentModelState = ModelState.NotReady;
    
        [SerializeField]
        [Tooltip("The friendly name for this model")]
        private string modelDisplayName;
        public override string ModelDisplayName { get => modelDisplayName; set => modelDisplayName = value; }
    
        [SerializeField]
        [Tooltip("The URI for this model")]
        private string modelPath;
        public override string ModelPath
        {
            get => modelPath.Trim();
            set => modelPath = value;
        }
    
        public override ModelState CurrentModelState
        {
            get => currentModelState;
            protected set
            {
                if (currentModelState != value)
                {
                    currentModelState = value;
                    ModelStateChange?.Invoke(value);
                }
            }
        }
    
        public override event Action<ModelState> ModelStateChange;
        public override event Action<float> LoadProgress;
        public override Entity ModelEntity { get; protected set; }
    
        public UnityEvent OnModelNotReady = new UnityEvent();
        public UnityEvent OnModelReady = new UnityEvent();
        public UnityEvent OnStartLoading = new UnityEvent();
        public UnityEvent OnModelLoaded = new UnityEvent();
        public UnityEvent OnModelUnloading = new UnityEvent();
    
        public UnityFloatEvent OnLoadProgress = new UnityFloatEvent();
    
        public void Awake()
        {
            // Hook up the event to the Unity event
            LoadProgress += (progress) => OnLoadProgress?.Invoke(progress);
    
            ModelStateChange += HandleUnityStateEvents;
        }
    
        private void HandleUnityStateEvents(ModelState modelState)
        {
            switch (modelState)
            {
                case ModelState.NotReady:  OnModelNotReady?.Invoke();  break;
                case ModelState.Ready:     OnModelReady?.Invoke();     break;
                case ModelState.Loading:   OnStartLoading?.Invoke();   break;
                case ModelState.Loaded:    OnModelLoaded?.Invoke();    break;
                case ModelState.Unloading: OnModelUnloading?.Invoke(); break;
            }
        }
    
        private void Start()
        {
            //Attach to and initialize current state (in case we're attaching late)
            RemoteRenderingCoordinator.CoordinatorStateChange += Instance_CoordinatorStateChange;
            Instance_CoordinatorStateChange(RemoteRenderingCoordinator.instance.CurrentCoordinatorState);
        }
    
        /// <summary>
        /// Listen for state changes on the coordinator, clean up this model's remote objects if we're no longer connected.
        /// Automatically load if required
        /// </summary>
        private void Instance_CoordinatorStateChange(RemoteRenderingCoordinator.RemoteRenderingState state)
        {
            switch (state)
            {
                case RemoteRenderingCoordinator.RemoteRenderingState.RuntimeConnected:
                    CurrentModelState = ModelState.Ready;
                    if (AutomaticallyLoad)
                        LoadModel();
                    break;
                default:
                    UnloadModel();
                    break;
            }
        }
    
        private void OnDestroy()
        {
            RemoteRenderingCoordinator.CoordinatorStateChange -= Instance_CoordinatorStateChange;
            UnloadModel();
        }
    
        /// <summary>
        /// Asks the coordinator to create a model entity and listens for coordinator state changes
        /// </summary>
        [ContextMenu("Load Model")]
        public override async void LoadModel()
        {
            if (CurrentModelState != ModelState.Ready)
                return; //We're already loaded, currently loading, or not ready to load
    
            CurrentModelState = ModelState.Loading;
    
            ModelEntity = await RemoteRenderingCoordinator.instance?.LoadModel(ModelPath, this.transform, SetLoadingProgress);
    
            if (ModelEntity != null)
                CurrentModelState = ModelState.Loaded;
            else
                CurrentModelState = ModelState.Error;
        }
    
        /// <summary>
        /// Clean up the local model instances
        /// </summary>
        [ContextMenu("Unload Model")]
        public override void UnloadModel()
        {
            CurrentModelState = ModelState.Unloading;
    
            if (ModelEntity != null)
            {
                var modelGameObject = ModelEntity.GetOrCreateGameObject(UnityCreationMode.DoNotCreateUnityComponents);
                Destroy(modelGameObject);
                ModelEntity.Destroy();
                ModelEntity = null;
            }
    
            if (RemoteRenderingCoordinator.instance.CurrentCoordinatorState == RemoteRenderingCoordinator.RemoteRenderingState.RuntimeConnected)
                CurrentModelState = ModelState.Ready;
            else
                CurrentModelState = ModelState.NotReady;
        }
    
        /// <summary>
        /// Update the Unity progress event
        /// </summary>
        /// <param name="progressValue"></param>
        public override void SetLoadingProgress(float progressValue)
        {
            LoadProgress?.Invoke(progressValue);
        }
    }
    

RemoteRenderedModel の役割は、一言で言えば、モデルの読み込みに必要なデータ (この場合は、SAS または builtin:// URI) を保持し、リモート モデルの状態を追跡することです。 モデルの読み込み時に、RemoteRenderingCoordinatorLoadModel メソッドが呼び出され、モデルを含むエンティティが参照とアンロード用に返されます。

テスト モデルを読み込む

もう一度テスト モデルを読み込んで、新しいスクリプトをテストしてみましょう。 このテストでは、スクリプトを含み、テスト モデルの親になる Game オブジェクトが必要です。また、モデルを含む仮想ステージも必要です。 ステージは、WorldAnchor を使用して、実際の世界を基準として固定されたままになります。 モデル自体を後で動かせるように、固定ステージを使用します。

  1. シーンに新しい空のゲーム オブジェクトを作成し、ModelStage という名前を付けます。

  2. ModelStage に WorldAnchor コンポーネントを追加します。

    WorldAnchor コンポーネントを追加する

  3. ModelStage の子として新しい空のゲーム オブジェクトを作成し、TestModel という名前を付けます。

  4. RemoteRenderedModel スクリプトを TestModel に追加します。

    RemoteRenderedModel コンポーネントを追加する

  5. [Model Display Name] と [Model Path] に、それぞれ「TestModel」と「builtin://Engine」を入力します。

    モデルの詳細を指定する

  6. カメラの前の位置 x = 0、y = 0、z = 3 に、TestModel オブジェクトを配置します。

    オブジェクトを配置する

  7. AutomaticallyLoad がオンになっていることを確認します。

  8. Unity エディターで [Play](再生) を押してアプリケーションをテストします。

  9. アプリがセッションを作成できるように [Connect](接続) ボタンをクリックして承認することで、セッションに接続し、自動的にモデルを読み込むようになります。

コンソールでアプリケーションの状態を監視します。 状態によっては完了までに時間がかかる場合があり、しばらくの間更新の進行状況が表示されないことに注意してください。 最終的には、モデルの読み込みからのログが表示され、すぐ後にシーンにレンダリングされたテスト モデルが表示されます。

インスペクターの [Transform](トランスフォーム)、または [Game] ビューで移動を観察する [Scene](シーン) ビューで、TestModel GameObject の移動や回転を試します。

Unity ログ

Azure に Blob Storage をプロビジョニングしてカスタム モデルを取り込む

ここで、自分のモデルを読み込んでみましょう。 そのためには、Blob Storage を構成し、Azure でモデルをアップロードして変換する必要があります。その後、RemoteRenderedModel スクリプトを使用してそのモデルを読み込みます。 この時点で読み込む独自のモデルがなければ、カスタム モデルの読み込みステップは省略してかまいません。

クイックスタート: モデルをレンダリング用に変換する」に記載の手順に従います。 このチュートリアルでは、「クイック スタート サンプル アプリに新しいモデルを挿入する」セクションはスキップしてください。 取り込んだモデルの Shared Access Signature (SAS) URI が得られたら、続行します。

カスタム モデルを読み込んでレンダリングする

  1. 新しい空の GameObject をシーンに作成し、カスタム モデルと同様の名前を付けます。

  2. 新しく作成した GameObject に RemoteRenderedModel スクリプトを追加します。

    RemoteRenderedModel コンポーネントを追加する

  3. Model Display Name に、カスタム モデルの適切な名前を入力します。

  4. Azure に Blob Storage をプロビジョニングしてカスタム モデルを取り込む手順で作成した、モデルの Shared Access Signature (SAS) URI を Model Path に入力します。

  5. カメラの前の x = 0, y = 0, z = 3 の位置に GameObject を配置します。

  6. AutomaticallyLoad がオンになっていることを確認します。

  7. Unity エディターで [Play](再生) を押してアプリケーションをテストします。

    コンソールには、セッションが接続されると、現在のセッション状態と進行状況についてのメッセージを読み込むモデルも表示されます。

  8. カスタム モデル オブジェクトをシーンから削除します。 このチュートリアルで最良のエクスペリエンスが得られるのは、テスト モデルを使用したときです。 ARR では複数のモデルがサポートされますが、このチュートリアルは、一度に 1 つのリモート モデルをサポートするよう最適化して作成されています。

次のステップ

これで、独自のモデルを Azure Remote Rendering に読み込んで、自分のアプリケーションで表示できるようになりました。 次に、モデルの操作方法について説明します。