Tutoriel : Interfaces et modèles personnalisés

Dans ce tutoriel, vous allez apprendre à :

  • Ajouter Mixed Reality Toolkit au projet
  • Gérer l’état du modèle
  • Configurer le stockage Blob Azure pour l’ingestion de modèles
  • Charger et traiter des modèles pour l’affichage

Prérequis

Bien démarrer avec MRTK (Mixed Reality Toolkit)

MRTK (Mixed Reality Toolkit) est un kit de ressources multiplateforme qui permet de générer des expériences de réalité mixte. Nous utilisons MRTK 2.8.3 pour ses fonctionnalités d’interaction et de visualisation.

Le guide officiel pour importer MRTK contient certaines étapes que nous n’avons pas besoin d’effectuer. Seules ces trois étapes sont nécessaires :

  • Importer « Mixed Reality Toolkit/Mixed Reality Toolkit Foundation » version 2.8.3 dans votre projet via l’outil de fonctionnalité Mixed Reality (Importer MRTK).
  • Exécuter l’assistant de configuration de MRTK (Configurer MRTK).
  • Ajouter MRTK à la scène actuelle (Ajouter à la scène). Ici, utilisez ARRMixedRealityToolkitConfigurationProfile, au lieu du profil suggéré dans le tutoriel.

Importer les ressources utilisées par ce tutoriel

À partir de ce chapitre, nous allons implémenter un schéma contrôleur-affichage-modèle simple pour la plupart des matériaux pris en charge. La partie modèle du schéma est le code propre à Azure Remote Rendering, et la gestion des états liés à Azure Remote Rendering. Les parties contrôleur et affichage du schéma sont implémentées à l’aide de ressources MRTK et de quelques scripts personnalisés. Il est possible d’utiliser le modèle dans ce tutoriel sans que les parties contrôleur-affichage soient implémentées ici. Cette séparation vous permet d’intégrer facilement le code trouvé dans ce tutoriel à votre propre application, dans laquelle il prendra le relais de la partie contrôleur-affichage du modèle de conception.

Avec l’introduction de MRTK, il existe de nombreux scripts, éléments préfabriqués et ressources qui peuvent désormais être ajoutés au projet pour prendre en charge les interactions et les commentaires visuels. Ces ressources, appelées Ressources du tutoriel, sont regroupées dans un Package de ressources Unity qui est présent dans le GitHub Azure Remote Rendering à cet emplacement : \Unity\TutorialAssets\TutorialAssets.unitypackage.

  1. Clonez ou téléchargez le dépôt Git Azure Remote Rendering ; si vous choisissez le téléchargement, extrayez le fichier zip à un emplacement connu.
  2. Dans votre projet Unity, choisissez Assets -> Import Package -> Custom Package (Ressources -> Importer un package -> Package personnalisé).
  3. Dans l’explorateur de fichiers, accédez au répertoire dans lequel vous avez cloné ou décompressé le référentiel Azure Remote Rendering, puis sélectionnez le .unitypackage trouvé dans Unity -> TutorialAssets ->TutorialAssets.unitypackage
  4. Sélectionnez le bouton Import pour importer le contenu du package dans votre projet.
  5. Dans l’éditeur Unity, sélectionnez Mixed Reality Toolkit -> Utilities (Utilitaires) -> Upgrade MRTK Standard Shader for Lightweight Render Pipeline (Mettre à niveau le nuanceur standard MRTK pour le pipeline de rendu léger) dans la barre de menus supérieure et suivez les invites pour mettre à niveau le nuanceur.

Une fois que MRTK et les ressources du tutoriel sont configurés, vérifiez une nouvelle fois que le profil approprié est sélectionné.

  1. Sélectionnez le GameObject MixedRealityToolkit dans la hiérarchie de la scène.
  2. Dans l’inspecteur, sous le composant MixedRealityToolkit, basculez le profil de configuration vers ARRMixedRealityToolkitConfigurationProfile.
  3. Appuyez sur les touches Ctrl+S pour enregistrer vos modifications.

MRTK sera ainsi principalement configuré avec les profils HoloLens 2 par défaut. Les profils fournis sont préconfigurés comme suit :

  • Désactivez le profileur (Appuyez sur 9 pour l’activer/le désactiver, ou dites « Show/Hide Profiler » (Afficher/masquer le profileur) sur l’appareil).
  • Désactivez le curseur en forme d’œil.
  • Activez les clics de souris Unity, afin de vous servir de la souris, au lieu de la main simulée, pour cliquer sur les éléments d’interface utilisateur MRTK.

Ajouter le menu de l’application

La plupart des contrôleurs d’affichage de ce tutoriel fonctionnent sur des classes de base abstraites plutôt que sur des classes concrètes. Ce modèle offre plus de flexibilité et nous permet de vous fournir les contrôleurs d’affichage tout en continuant de vous aider dans votre apprentissage du code Azure Remote Rendering. Par souci de simplicité, la classe RemoteRenderingCoordinator n’a pas de classe abstraite fournie et son contrôleur d’affichage fonctionne directement sur la classe concrète.

Vous pouvez maintenant ajouter l’élément préfabriqué AppMenu à la scène pour obtenir un retour visuel de l’état de la session en cours. Le AppMenu présente également le panneau modal que l’utilisateur utilise pour autoriser l’application à se connecter à ARR.

  1. Localisez l’élément préfabriqué AppMenu dans Assets/RemoteRenderingTutorial/Prefabs/AppMenu.

  2. Faites glisser l’élément préfabriqué AppMenu dans la scène.

  3. Si vous voyez une boîte de dialogue pour Importateur TMP, suivez les invites pour Importer TMP Essentials. Fermez ensuite la boîte de dialogue de l’importateur, les exemples et les suppléments n’étant pas nécessaires.

  4. AppMenu est configuré pour se raccorder automatiquement et fournir le modal de consentement pour la connexion à une session ; nous pouvons donc supprimer le contournement placé précédemment. Dans le GameObject RemoteRenderingCoordinator, supprimez le contournement pour l’autorisation que nous avons implémenté auparavant en sélectionnant le bouton « - » dans l’événement On Requesting Authorization (Demande d’autorisation).

    Supprimer le contournement.

  5. Testez le contrôleur d’affichage en appuyant sur Play (Lecture) dans l’éditeur Unity.

  6. Dans l’éditeur, à présent que MRTK est configuré, vous pouvez utiliser les touches WASD pour modifier la position de votre affichage, et maintenir le bouton droit de la souris enfoncé pendant le déplacement de la souris pour modifier la direction de l’affichage. Essayez « d’explorer » un peu la scène pour vous faire une idée des contrôles.

  7. Sur l’appareil, vous pouvez lever la paume pour appeler AppMenu ; dans l’éditeur Unity, utilisez la touche d’accès rapide « M ».

  8. Si vous ne voyez plus le menu, appuyez sur la touche « M » pour le faire apparaître. Le menu est placé près de la caméra pour faciliter l’interaction.

  9. Le AppMenu présente un élément d’interface utilisateur pour l’autorisation, à droite du AppMenu. À partir de maintenant, vous devez utiliser cet élément d’interface utilisateur pour autoriser l’application à gérer les sessions de rendu à distance.

    Autoriser l’IU

  10. Arrêtez la lecture de Unity pour continuer le tutoriel.

Gérer l’état du modèle

Nous avons besoin d’un nouveau script appelé RemoteRenderedModel, qui est utilisé pour le suivi d’état, la réponse aux évènements, le déclenchement des évènements et la configuration. Pour l’essentiel, RemoteRenderedModel stocke le chemin distant des données du modèle dans modelPath. Il écoute les modifications d’état dans le RemoteRenderingCoordinator pour voir s’il doit charger ou décharger automatiquement le modèle qu’il définit. Le GameObject auquel est attaché le RemoteRenderedModel est le parent local du contenu distant.

Notez que le script RemoteRenderedModel implémente BaseRemoteRenderedModel, inclus dans les Tutorial Assets (Ressources du tutoriel). Cette connexion permet au contrôleur d’affichage du modèle distant de se lier à votre script.

  1. Créez un script nommé RemoteRenderedModel dans le même dossier que RemoteRenderingCoordinator. Remplacez l’intégralité du contenu par le code suivant :

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

Pour faire simple, RemoteRenderedModel contient les données nécessaires au chargement d’un modèle (dans le cas présent, l’URI SAS ou builtin:// ) et effectue le suivi de l’état du modèle distant. Au moment du chargement du modèle, la méthode LoadModel est appelée sur RemoteRenderingCoordinator et l’Entité contenant le modèle est retournée pour référence et déchargement.

Charger le modèle de test

Testons le nouveau script en chargeant à nouveau le modèle de test. Pour ce test, nous avons besoin d’un Game Object pour contenir le script et être parent du modèle de test, et nous avons également besoin d’une scène virtuelle qui contient le modèle. La scène reste fixe par rapport au monde réel grâce à un WorldAnchor. Nous utilisons une étape fixe pour que le modèle puisse être déplacé par la suite.

  1. Créez un objet de jeu vide dans la scène et nommez-le ModelStage.

  2. Ajouter un composant WorldAnchor à ModelStage

    Ajouter un composant WorldAnchor

  3. Créez un objet de jeu vide en tant qu’enfant de ModelStage et nommez-le TestModel.

  4. Ajoutez le script RemoteRenderedModel à TestModel.

    Ajouter le composant RemoteRenderedModel

  5. Renseignez Model Display Name et Model Path en utilisant respectivement « TestModel » et « builtin://Engine ».

    Renseigner les détails du modèle

  6. Placez l’objet TestModel devant la caméra, à la position x = 0, y = 0, z = 3.

    Positionner l’objet

  7. Assurez-vous que AutomaticallyLoad est activé.

  8. Appuyez sur Play (Lecture) dans l’éditeur Unity pour tester l’application.

  9. Octroyez l’autorisation en cliquant sur le bouton Connexion, pour permettre à l’application de créer une session, de s’y connecter et de charger automatiquement le modèle.

Regardez la console tandis que l’application progresse dans ses différents états. Gardez à l’esprit que certains états peuvent prendre un certain temps à être atteints et qu’il peut ne pas y avoir de mises à jour de la progression pendant un certain temps. À la fin, vous voyez les journaux de chargement du modèle et, rapidement, le modèle de test est rendu dans la scène.

Essayez de déplacer et de faire pivoter le GameObject TestModel à l’aide de l’outil Transformation dans Inspecteur, ou dans l’affichage Scène et observez les transformations dans l’affichage Jeu.

Journal Unity

Provisionner le stockage Blob dans Azure et l’ingestion de modèle personnalisé

Nous pouvons à présent essayer de charger votre propre modèle. Pour ce faire, vous devez configurer le stockage d’objets Blob sur Azure, charger puis convertir un modèle et le charger ensuite à l’aide du script RemoteRenderedModel. Les étapes de chargement du modèle personnalisé peuvent être ignorées sans risque si vous ne disposez pas de votre propre modèle à charger pour l’instant.

Suivez les étapes indiquées dans le Guide de démarrage rapide : Convertir un modèle pour le rendu. Vous pouvez ignorer la section Insérer un nouveau modèle dans l’exemple d’application de démarrage rapide de ce tutoriel. Lorsque vous disposez de l’URI SAP (signature d’accès partagé) de votre modèle ingéré, continuez.

Charger et afficher un modèle personnalisé

  1. Créez un GameObject vide dans la scène et nommez-le comme votre modèle personnalisé.

  2. Ajoutez le script RemoteRenderedModel au GameObject nouvellement créé.

    Ajouter le composant RemoteRenderedModel

  3. Renseignez Model Display Name en utilisant un nom approprié pour votre modèle.

  4. Renseignez le Model Path avec l’URI de signature d’accès partagé (SAP) du modèle que vous avez créé à l’étape Approvisionner le stockage Blob dans Azure et ingérer un modèle personnalisé.

  5. Placez le GameObject devant la caméra, à la position x = 0, y = 0, z = 3.

  6. Assurez-vous que AutomaticallyLoad est activé.

  7. Appuyez sur Play (Lecture) dans l’éditeur Unity pour tester l’application.

    La console affiche l’état actuel de la session, ainsi que les messages de progression du chargement du modèle, une fois la session connectée.

  8. Supprimez de la scène l’objet de votre modèle personnalisé. Utiliser le modèle de test garantit la meilleure expérience avec ce tutoriel. Bien que plusieurs modèles soient pris en charge dans ARR, ce tutoriel a été écrit pour optimiser la prise en charge d’un seul modèle distant à la fois.

Étapes suivantes

Vous pouvez désormais charger vos propres modèles dans Azure Remote Rendering et les afficher dans votre application ! À suivre, nous vous guidons dans la manipulation de vos modèles.