Esercitazione: Interfacce e modelli personalizzati

In questa esercitazione verranno illustrate le procedure per:

  • Aggiungere Mixed Reality Toolkit al progetto
  • Gestire lo stato dei modelli
  • Configurare Archiviazione BLOB di Azure per l'inserimento di modelli
  • Caricare ed elaborare i modelli per il rendering

Prerequisiti

Introduzione a Mixed Reality Toolkit (MRTK)

Mixed Reality Toolkit (MRTK) è un toolkit multipiattaforma per la creazione di esperienze di realtà mista. Si usa MRTK 2.8.3 per le funzionalità di interazione e visualizzazione.

La guida ufficiale all'importazione di MRTK contiene alcuni passaggi che non è necessario eseguire. Sono necessari solo questi tre passaggi:

  • Importazione di 'Realtà mista Toolkit/Realtà mista Toolkit Foundation' versione 2.8.3 nel progetto tramite lo strumento di funzionalità Realtà mista (Importa MRTK).
  • Eseguire la configurazione guidata di MRTK (Configurare MRTK).
  • Aggiungere MRTK alla scena corrente (Aggiungi alla scena). Usare ARRMixedRealityToolkitConfigurationProfile qui anziché il profilo suggerito nell'esercitazione.

Importare gli asset usati da questa esercitazione

A partire da questo capitolo, si implementerà un modello basic model-view-controller per gran parte del materiale coperto. La parte model del modello è il codice specifico per Rendering remoto di Azure e la gestione dello stato correlata a Rendering remoto di Azure. Le parti view e controller del modello vengono implementate tramite asset di MRTK e alcuni script personalizzati. È possibile usare il modello in questa esercitazione senza che il controller di visualizzazione sia implementato qui. Questa separazione consente di integrare facilmente il codice trovato in questa esercitazione nell'applicazione in cui accetta la parte del controller di visualizzazione del modello di progettazione.

Con l'introduzione di MRTK, sono disponibili più script, prefab e asset che ora possono essere aggiunti al progetto per supportare interazioni e feedback visivi. Questi asset denominati Asset esercitazione vengono raggruppati in un pacchetto di asset unity, incluso in GitHub di Azure Rendering remoto in '\Unity\TutorialAssets\TutorialAssets.unitypackage'.

  1. Clonare o scaricare il repository git Rendering remoto di Azure, se il download estrae lo ZIP in una posizione nota.
  2. Nel progetto Unity scegliere Asset -> Importa pacchetto -> Pacchetto personalizzato.
  3. In Esplora file passare alla directory in cui è stato clonato o scollegato il repository di Azure Rendering remoto, quindi selezionare il .unitypackage file disponibile in Unity - TutorialAssets ->> TutorialAssets.unitypackage
  4. Selezionare il pulsante Import (Importa) per integrare il contenuto del pacchetto nel progetto.
  5. Nell'editor di Unity selezionare Realtà mista Toolkit - Utilità ->> Aggiornare MRTK Standard Shader per La pipeline di rendering leggero dalla barra dei menu in alto e seguire le istruzioni per aggiornare lo shader.

Dopo che MRTK e gli asset esercitazione vengono configurati doppio controllo, che il profilo corretto è selezionato.

  1. Selezionare il GameObject MixedRealityToolkit nella gerarchia della scena.
  2. Nella finestra di controllo, nel componente MixedRealityToolkit, impostare il profilo di configurazione su ARRMixedRealityToolkitConfigurationProfile.
  3. Premere CTRL+S per salvare le modifiche.

Questo passaggio configura MRTK, principalmente, con i profili di HoloLens 2 predefiniti. I profili forniti sono preconfigurati nei modi seguenti:

  • Disattivare il profiler. Premere 9 per attivarlo o disattivarlo oppure selezionare "Show/Hide Profiler" (Mostra/nascondi profiler) nel dispositivo.
  • Disattivare il cursore dello sguardo.
  • Abilitare i clic del mouse di Unity, in modo che sia possibile fare clic sugli elementi dell'interfaccia utente di MRTK con il mouse invece che con la mano simulata.

Aggiungere il menu di app

La maggior parte dei controller di visualizzazione di questa esercitazione opera su classi di base astratte invece che concrete. Questo modello offre una maggiore flessibilità e consente di fornire i controller di visualizzazione, semplificando al tempo stesso l'apprendimento del codice di Rendering remoto di Azure. Per semplicità, la classe RemoteRenderingCoordinator non ha una classe astratta fornita e il relativo controller di visualizzazione opera direttamente sulla classe concreta.

È ora possibile aggiungere il prefab AppMenu alla scena, per il feedback visivo dello stato della sessione corrente. AppMenu presenta anche il pannello modale usato dall'utente per autorizzare l'applicazione a connettersi a ARR.

  1. Individuare il prefab AppMenu in Assets/RemoteRenderingTutorial/Prefabs/AppMenu

  2. Trascinare il prefab AppMenu nella scena.

  3. Se viene visualizzata una finestra di dialogo per L'importazione TMP, seguire le istruzioni per importare TMP Essentials. Chiudere quindi la finestra di dialogo di importazione, poiché gli esempi e gli extra non sono necessari.

  4. AppMenu è configurato per l'associazione automatica e fornisce la finestra di dialogo modale per acconsentire alla connessione a una sessione, quindi è possibile rimuovere il bypass inserito in precedenza. Nel GameObject RemoteRenderingCoordinator rimuovere il bypass per l'autorizzazione implementato in precedenza, premendo il pulsante '-' nell'evento On Requesting Authorization.

    Rimuovere il bypass.

  5. Testare il controller di visualizzazione premendo Play nell'editor di Unity.

  6. Nell'editor, ora che MRTK è configurato, è possibile usare i tasti WASD per cambiare la posizione della visualizzazione e tenere premuto il pulsante destro del mouse spostando contemporaneamente il mouse per cambiare la direzione di visualizzazione. Provare a muoversi nella scena per acquisire familiarità con i controlli.

  7. Nel dispositivo è possibile sollevare il palmo per richiamare AppMenu. Nell'editor di Unity usare il tasto di scelta rapida 'M'.

  8. Se il menu non viene più visualizzato, premere 'M' per richiamarlo. Il menu viene posizionato vicino alla fotocamera per facilitare l'interazione.

  9. AppMenu presenta un elemento dell'interfaccia utente per l'autorizzazione a destra di AppMenu. Da ora in poi, è consigliabile usare questo elemento dell'interfaccia utente per autorizzare l'app a gestire le sessioni di rendering remoto.

    Autorizzazione dell'interfaccia utente

  10. Interrompere la riproduzione di Unity per continuare con l'esercitazione.

Gestire lo stato dei modelli

È necessario un nuovo script denominato RemoteRenderedModel che è per il rilevamento dello stato, la risposta agli eventi, l'attivazione di eventi e la configurazione. Essenzialmente, RemoteRenderedModel archivia il percorso remoto per i dati del modello in modelPath. È in ascolto delle modifiche dello stato nell'oggetto RemoteRenderingCoordinator per verificare se deve caricare o scaricare automaticamente il modello che definisce. GameObject con RemoteRenderedModel collegato a esso è l'elemento padre locale per il contenuto remoto.

Si noti che lo script RemoteRenderedModel implementa BaseRemoteRenderedModel, incluso dagli asset dell'esercitazione. Questa connessione consente al controller di visualizzazione modello remoto di associare lo script.

  1. Creare un nuovo script denominato RemoteRenderedModel nella stessa cartella di RemoteRenderingCoordinator. Sostituire l'intero contenuto con il codice seguente:

    // 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);
        }
    }
    

In termini più semplici, RemoteRenderedModel include i dati necessari per caricare un modello (in questo caso l'URI di firma di accesso condiviso o builtin:// ) e tiene traccia dello stato del modello remoto. Quando è tempo di caricare il modello, il LoadModel metodo viene chiamato in RemoteRenderingCoordinator e l'entità contenente il modello viene restituita per riferimento e scaricamento.

Caricare il modello di test

È ora necessario testare il nuovo script caricando di nuovo il modello di test. Per questo test è necessario un oggetto Game per contenere lo script e essere un elemento padre del modello di test e è necessaria anche una fase virtuale contenente il modello. La fase rimane fissa rispetto al mondo reale usando un WorldAnchor. Viene usata una fase fissa in modo che il modello stesso possa comunque essere spostato più avanti.

  1. Creare un nuovo oggetto gioco vuoto nella scena e assegnarne il nome ModelStage.

  2. Aggiungere un componente Di ancoraggio mondiale a ModelStage

    Aggiungere il componente WorldAnchor

  3. Creare un nuovo oggetto game vuoto come figlio di ModelStage e assegnarne il nome TestModel.

  4. Aggiungere lo script RemoteRenderedModel a TestModel.

    Aggiungere il componente RemoteRenderedModel

  5. Compilare Model Display Name e Model Path rispettivamente con "TestModel" e "builtin://Engine".

    Specificare i dettagli del modello

  6. Posizionare l'oggetto TestModel davanti alla videocamera, nella posizione x = 0, y = 0, z = 3.

    Oggetto Position

  7. Assicurarsi che AutomaticallyLoad sia attivato.

  8. Premere Play nell'editor di Unity per testare l'applicazione.

  9. Concedere l'autorizzazione facendo clic sul pulsante Connetti per consentire all'app di creare una sessione, connettersi e caricare automaticamente il modello.

Osservare la console mentre l'applicazione avanza attraverso i relativi stati. Tenere presente che alcuni stati potrebbero richiedere tempo per completare e potrebbero non esserci aggiornamenti dello stato per un certo periodo. Alla fine, vengono visualizzati i log dal caricamento del modello e quindi poco dopo il modello di test sottoposto a rendering nella scena.

Provare a spostare e ruotare TestModel GameObject tramite la trasformazione nel controllo o nella visualizzazione Scena e osservare le trasformazioni nella visualizzazione Gioco.

Log di Unity

Provisioning di Archiviazione BLOB di Azure e inserimento del modello personalizzato

A questo punto è possibile provare a caricare il proprio modello. A tale scopo, è necessario configurare Archiviazione BLOB in Azure, caricare e convertire un modello e quindi caricare il modello usando lo script RemoteRenderedModel . I passaggi di caricamento del modello personalizzato possono essere tranquillamente ignorati se al momento non è disponibile un proprio modello.

Seguire i passaggi specificati in Avvio rapido: Convertire un modello per il rendering. Ignorare la sezione Inserisci nuovo modello nell'app di esempio di avvio rapido per questa esercitazione. Dopo aver inserito l'URI firma di accesso condiviso (SAS), continuare.

Caricamento e rendering di un modello personalizzato

  1. Creare un nuovo GameObject vuoto nella scena e assegnargli un nome simile a quello del modello personalizzato.

  2. Aggiungere lo script RemoteRenderedModel al GameObject appena creato.

    Aggiungere il componente RemoteRenderedModel

  3. Compilare Model Display Name con un nome appropriato per il modello.

  4. Compilare l'URI Model Path con firma di accesso condiviso del modello creato nel passaggio Di archiviazione BLOB di provisioning in Azure e nel passaggio di inserimento del modello personalizzato .

  5. Posizionare il GameObject davanti alla videocamera, nella posizione x = 0, y = 0, z = 3.

  6. Assicurarsi che AutomaticallyLoad sia attivato.

  7. Premere Play nell'editor di Unity per testare l'applicazione.

    La console mostra lo stato della sessione corrente e anche il modello carica i messaggi di stato, una volta connessa la sessione.

  8. Rimuovere l'oggetto modello personalizzato dalla scena. L'esperienza migliore per questa esercitazione è con il modello di test. Mentre più modelli sono supportati in ARR, questa esercitazione è stata scritta per supportare al meglio un singolo modello remoto alla volta.

Passaggi successivi

È ora possibile caricare i propri modelli in Rendering remoto di Azure e visualizzarli nell'applicazione. Verrà quindi illustrato come modificare i modelli.