Tutoriel : Affichage d’un modèle rendu à distance

Dans ce tutoriel, vous allez apprendre à :

  • Provisionner une instance Azure Remote Rendering (ARR)
  • Créer et arrêter une session de rendu
  • Réutiliser une session de rendu existante
  • Se connecter et se déconnecter de sessions
  • Charger des modèles dans une session de rendu

Prérequis

Pour ce tutoriel, vous avez besoin des éléments suivants :

Provisionner une instance Azure Remote Rendering (ARR)

Pour pouvoir accéder au service Azure Remote Rendering, vous devez d’abord créer un compte.

Créer un projet Unity

Conseil

Le dépôt d’exemples ARR contient un projet dans lequel tous les tutoriels ont été effectués ; il peut servir de référence. Le projet Unity complet se trouve dans Unity\Tutorial-Complete.

Dans Unity Hub, créez un projet. Dans cet exemple, nous partons du principe que le projet est créé dans un dossier nommé RemoteRendering.

Screenshot of Unity Hub showing the Create a New Unity Project dialog. The panel 3D is selected.

Ajouter les packages Azure Remote Rendering et OpenXR

Suivez les instructions pour ajouter les packages Azure Remote Rendering et OpenXR à un projet Unity.

Notes

Si Unity affiche un avertissement, après l’importation du package OpenXR, demandant s’il faut activer les back-ends natifs de plateforme pour le nouveau système d’entrée, cliquez pour le moment sur Non. Vous allez l’activer à une étape ultérieure.

Configurer la caméra

  1. Sélectionner le nœud Main Camera (Caméra principale).

  2. Ouvrez le menu contextuel en cliquant avec le bouton droit sur le composant Transform et sélectionnez l’option Reset :

    Screenshot of the Unity inspector for a Transform component. The context menu is opened and Reset is selected.

  3. Définissez Clear flags (Effacer les indicateurs) sur Solid Color (Couleur unie).

  4. Définissez Background (Arrière-plan) sur Black (Noir) (#000000) en sélectionnant la valeur d’alpha (A) complètement transparente (0).

    Screenshot of the Unity Color wheel dialog. The color is set to 0 for all R G B A components.

  5. Définissez Clipping Planes (Plans de découpage) sur Near = 0.1 (Proche = 0,3) et Far = 20 (Lointain = 20). Cette configuration signifie que le rendu détoure la géométrie qui est à moins de 10 cm ou à plus de 20 mètres.

    Screenshot of the Unity inspector for a Camera component.

Ajuster les paramètres du projet

  1. Ouvrez Modifier > Paramètres du projet...

  2. Sélectionnez Quality (Qualité) dans le menu de liste de gauche.

    1. Définissez Default Quality Level (Niveau de qualité par défaut) de toutes les plateformes sur Low (Bas). Ce paramètre assure un rendu plus efficace du contenu local et n’affecte pas la qualité du contenu affiché à distance.

      Screenshot of the Unity Project Settings dialog. The Quality entry is selected in the list on the left. The context menu for the default quality level is opened on the right. The low entry is selected.

    Remarque

    Dans l’étendue de ce tutoriel, nous nous en tenons au pipeline de rendu intégré Unity. Si vous souhaitez utiliser le pipeline de rendu universel, consultez Pipelines de rendu Unity pour connaître les étapes de configuration supplémentaires.

  3. Sélectionnez XR Plugin Management dans le menu de la liste de gauche

    1. Cliquez sur le bouton Install XR Plugin Management.
    2. Sélectionnez l’onglet Universal Windows Platform settings (Paramètres de la plateforme Windows universelle), représenté par une icône Windows.
    3. Cochez la case Open XR sous Fournisseurs de plug-in
    4. Si une boîte de dialogue s’ouvre et vous demande d’activer les back-ends de la plateforme native pour le nouveau système d’entrée, sélectionnez Non.

    Screenshot of the Unity Project Settings dialog. The XR Plug-in Management entry is selected in the list on the left. The tab with the windows logo is highlighted on the right. The Open XR checkbox below it is also highlighted.

    Remarque

    Si Microsoft HoloLens feature group est désactivé, le plug-in OpenXR Windows Mixed Reality est manquant dans votre projet. Suivez les instructions pour ajouter les packages Azure Remote Rendering et OpenXR afin de l’installer.

  4. Sélectionnez OpenXR dans le menu de la liste de gauche

    1. Définissez Depth Submission Mode sur Depth 16 Bit
    2. Ajoutez le Microsoft Hand Interaction Profile à Interaction Profiles.
    3. Activez ces fonctionnalités OpenXR :
      • Azure Remote Rendering
      • Suivi de la main
      • Fonctionnalités de réalité mixte
      • Modèle de contrôleur de mouvement

    Screenshot of the Unity Project Settings dialog. The Open XR subentry is selected in the list on the left. Highlights on the right side are placed on the Depth Submission Mode, Interaction Profiles, and Open XR feature settings.

    Remarque

    Si les fonctionnalités OpenXR nécessaires ne sont pas listées, le plug-in OpenXR Windows Mixed Reality est manquant dans votre projet. Suivez les instructions pour ajouter les packages Azure Remote Rendering et OpenXR afin de l’installer.

  5. Sélectionnez Player (Lecteur) dans le menu de liste de gauche.

    1. Sélectionnez l’onglet Universal Windows Platform settings (Paramètres de la plateforme Windows universelle), représenté par une icône Windows.
    2. Développez Other settings
    3. Sous Rendering, définissez Color Space sur Linear et redémarrez Unity quand vous y êtes invité.
    4. Sous Configuration, définissez Active Input Handling sur Both et redémarrez Unity quand vous y êtes invité. Screenshot of the Unity Project Settings dialog. The Player entry is selected in the list on the left. Highlights on the right side are placed on the tab with the Windows logo, the Color Space setting, and the Active input Handling setting.
    5. Développez Publishing Settings
    6. Faites défiler jusqu’à Capabilities (Fonctionnalités), puis sélectionnez :
      • InternetClient
      • InternetClientServer
      • SpatialPerception
      • PrivateNetworkClientServer (facultatif). Sélectionnez cette option si vous souhaitez connecter le débogueur distant Unity à votre appareil.
    7. Sous Supported Device Families, activez Holographic et DesktopScreenshot of the Unity Project Settings dialog. The Player entry is selected in the list on the left. Highlights on the right side are placed on the Capabilities and the Supported Device Families settings.
  6. Fermez ou ancrez le panneau Project Settings (Paramètres du projet).

  7. Ouvrez File->Build Settings

    1. Sélectionnez Universal Windows Platform (Plateforme Windows universelle).
    2. Configurez vos paramètres tels qu’ils sont définis ci-dessous.
    3. Appuyez sur le bouton Switch Platform.
      Screenshot of the Unity Build Settings dialog. The Universal Windows Platform entry is selected in the list on the left. Highlights on the right side are placed on the settings dropdown boxes and the Switch Platform button.
  8. Une fois que Unity a changé de plateforme, fermez le panneau de génération.

Valider la configuration du projet

Effectuez les étapes suivantes pour vérifier que les paramètres du projet sont corrects.

  1. Choisissez l’entrée ValidateProject dans le menu RemoteRendering de la barre d’outils de l’éditeur Unity.

  2. Vérifiez que la fenêtre du Validateur de projet ne contient pas d’erreurs et corrigez les paramètres du projet si nécessaire.

    Screenshot of the Unity Project Validator dialog. The dialog shows a list of required, recommended, and development rules that are all successful checked.

Remarque

Si vous utilisez MRTK dans votre projet et que vous activez le sous-système de caméra, MRTK remplace les modifications manuelles que vous appliquez à la caméra. Sont inclus les correctifs de l’outil ValidateProject.

Créer un script pour coordonner la connexion et l’état d’Azure Remote Rendering

L’affichage des modèles rendus à distance se fait en quatre phases, comme le montre le diagramme de flux ci-dessous. Chaque phase doit être effectuée dans l’ordre. L’étape suivante consiste à créer un script qui gère l’état de l’application et passe par chaque phase obligatoire.

Diagram of the four stages required to load a model.

  1. Dans le volet Project (Projet), sous Assets (Ressources), créez un dossier sous le nom RemoteRenderingCore. Ensuite, à l’intérieur de RemoteRenderingCore, créez un autre dossier sous le nom Scripts.

  2. Créez un nouveau script C# sous le nom RemoteRenderingCoordinator. Votre programme doit se présenter comme suit :

    Screenshot of Unity Project hierarchy containing the new script.

    Ce script coordinateur suit et gère l’état du rendu à distance. Notez qu’une partie de ce code sert à gérer l’état, à exposer les fonctionnalités à d’autres composants, à déclencher des événements et à stocker des données propres à l’application qui ne sont pas directement liées à Azure Remote Rendering. Utilisez le code ci-dessous comme point de départ et nous aborderons et implémenterons le code propre à Azure Remote Rendering plus loin dans ce tutoriel.

  3. Ouvrez RemoteRenderingCoordinator dans votre éditeur de code et remplacez tout son contenu par le code ci-dessous :

// 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 System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;

#if UNITY_WSA
using UnityEngine.XR.WSA;
#endif

/// <summary>
/// Remote Rendering Coordinator is the controller for all Remote Rendering operations.
/// </summary>

// Require the GameObject with a RemoteRenderingCoordinator to also have the ARRServiceUnity component
[RequireComponent(typeof(ARRServiceUnity))]
public class RemoteRenderingCoordinator : MonoBehaviour
{
    public enum RemoteRenderingState
    {
        NotSet,
        NotInitialized,
        NotAuthorized,
        NoSession,
        ConnectingToExistingRemoteSession,
        ConnectingToNewRemoteSession,
        RemoteSessionReady,
        ConnectingToRuntime,
        RuntimeConnected
    }

    public static RemoteRenderingCoordinator instance;

    // Account
    // RemoteRenderingDomain must be '<region>.mixedreality.azure.com' - if no '<region>' is specified, connections will fail
    // For most people '<region>' is either 'westus2' or 'westeurope'
    [SerializeField]
    private string remoteRenderingDomain = "westus2.mixedreality.azure.com";
    public string RemoteRenderingDomain
    {
        get => remoteRenderingDomain.Trim();
        set => remoteRenderingDomain = value;
    }

    [Header("Development Account Credentials")]
    [SerializeField]
    private string accountDomain = "<enter your account domain here>";
    public string AccountDomain
    {
        get => accountDomain.Trim();
        set => accountDomain = value;
    }

    [SerializeField]
    private string accountId = "<enter your account id here>";
    public string AccountId {
        get => accountId.Trim();
        set => accountId = value;
    }

    [SerializeField]
    private string accountKey = "<enter your account key here>";
    public string AccountKey {
        get => accountKey.Trim();
        set => accountKey = value;
    }

    // These settings are important. All three should be set as low as possible, while maintaining a good user experience
    // See the documentation around session management and the technical differences in session VM size
    [Header("New Session Defaults")]

    public RenderingSessionVmSize renderingSessionVmSize = RenderingSessionVmSize.Standard;
    public uint maxLeaseHours = 0;
    public uint maxLeaseMinutes = 20;

    [Header("Other Configuration")]

    [Tooltip("If you have a known active SessionID, you can fill it in here before connecting")]
    [SerializeField]
    private string sessionIDOverride;
    public string SessionIDOverride {
        get => sessionIDOverride.Trim();
        set => sessionIDOverride = value;
    }

    // When Automatic Mode is true, the coordinator will attempt to automatically proceed through the process of connecting and loading a model
    public bool automaticMode = true;

    public event Action RequestingAuthorization;
    public UnityEvent OnRequestingAuthorization = new UnityEvent();

    public event Action AuthorizedChanged;
    public UnityEvent OnAuthorizationChanged = new UnityEvent();
    private bool authorized;
    public bool Authorized
    {
        get => authorized;
        set
        {
            if (value == true) //This is a one-way value, once we're authorized it lasts until the app is shutdown.
            {
                authorized = value;
                AuthorizedChanged?.Invoke();
            }
        }
    }

    public delegate Task<SessionConfiguration> AccountInfoGetter();

    public static AccountInfoGetter ARRCredentialGetter
    {
        private get;
        set;
    }

    private RemoteRenderingState currentCoordinatorState = RemoteRenderingState.NotSet;
    public RemoteRenderingState CurrentCoordinatorState
    {
        get => currentCoordinatorState;
        private set
        {
            if (currentCoordinatorState != value)
            {
                currentCoordinatorState = value;
                Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "{0}", $"State changed to: {currentCoordinatorState}");
                CoordinatorStateChange?.Invoke(currentCoordinatorState);
            }
        }
    }

    public static event Action<RemoteRenderingState> CoordinatorStateChange;

    public static RenderingSession CurrentSession => instance?.ARRSessionService?.CurrentActiveSession;

    private ARRServiceUnity arrSessionService;

    private ARRServiceUnity ARRSessionService
    {
        get
        {
            if (arrSessionService == null)
                arrSessionService = GetComponent<ARRServiceUnity>();
            return arrSessionService;
        }
    }

    private async Task<SessionConfiguration> GetDevelopmentCredentials()
    {
        Debug.LogWarning("Using development credentials! Not recommended for production.");
        return await Task.FromResult(new SessionConfiguration(AccountDomain, RemoteRenderingDomain, AccountId, AccountKey));
    }

    /// <summary>
    /// Keep the last used SessionID, when launching, connect to this session if its available
    /// </summary>
    private string LastUsedSessionID
    {
        get
        {
            if (!string.IsNullOrEmpty(SessionIDOverride))
                return SessionIDOverride;

            if (PlayerPrefs.HasKey("LastUsedSessionID"))
                return PlayerPrefs.GetString("LastUsedSessionID");
            else
                return null;
        }
        set
        {
            PlayerPrefs.SetString("LastUsedSessionID", value);
        }
    }

    public void Awake()
    {
        //Forward events to Unity events
        RequestingAuthorization += () => OnRequestingAuthorization?.Invoke();
        AuthorizedChanged += () => OnAuthorizationChanged?.Invoke();

        //Attach to event
        AuthorizedChanged += RemoteRenderingCoordinator_AuthorizedChanged;

        if (instance == null)
            instance = this;
        else
            Destroy(this);

        CoordinatorStateChange += AutomaticMode;

        CurrentCoordinatorState = RemoteRenderingState.NotInitialized;
    }

    private void RemoteRenderingCoordinator_AuthorizedChanged()
    {
        if (CurrentCoordinatorState != RemoteRenderingState.NotAuthorized)
            return; //This isn't valid from any other state than NotAuthorized


        //We just became authorized to connect to Azure
        InitializeSessionService();
    }

    /// <summary>
    /// Automatic mode attempts to automatically progress through the connection and loading steps. Doesn't handle error states.
    /// </summary>
    /// <param name="currentState">The current state</param>
    private async void AutomaticMode(RemoteRenderingState currentState)
    {
        if (!automaticMode)
            return;

        //Add a small delay for visual effect
        await Task.Delay(1500);
        switch (currentState)
        {
            case RemoteRenderingState.NotInitialized:
                InitializeARR();
                break;
            case RemoteRenderingState.NotAuthorized:
                RequestAuthorization();
                break;
            case RemoteRenderingState.NoSession:
                JoinRemoteSession();
                break;
            case RemoteRenderingState.RemoteSessionReady:
                ConnectRuntimeToRemoteSession();
                break;
        }
    }

    /// <summary>
    /// Initializes ARR, associating the main camera
    /// Note: This must be called on the main Unity thread
    /// </summary>
    public void InitializeARR()
    {
        //Implement me
    }

    /// <summary>
    /// Create a new remote session manager
    /// If the ARRCredentialGetter is set, use it as it, otherwise use the development credentials 
    /// </summary>
    public async void InitializeSessionService()
    {
        //Implement me
    }

    /// <summary>
    /// Trigger the event for checking authorization, respond to this event by prompting the user for authentication
    /// If authorized, set Authorized = true
    /// </summary>
    public void RequestAuthorization()
    {
        RequestingAuthorization?.Invoke();
    }

    public void BypassAuthorization()
    {
        Authorized = true;
    }

    /// <summary>
    /// Attempts to join an existing session or start a new session
    /// </summary>
    public async void JoinRemoteSession()
    {
        //Implement me
    }

    public async void StopRemoteSession()
    {
        //Implement me
    }

    private async Task<bool> IsSessionAvailable(string sessionID)
    {
        bool sessionAvailable = false;
        try
        {
            RenderingSessionPropertiesArrayResult result = await ARRSessionService.Client.GetCurrentRenderingSessionsAsync();
            if (result.ErrorCode == Result.Success)
            {
                RenderingSessionProperties[] properties = result.SessionProperties;
                if (properties != null)
                {
                    sessionAvailable = properties.Any(x => x.Id == sessionID && (x.Status == RenderingSessionStatus.Ready || x.Status == RenderingSessionStatus.Starting));
                }
            }
            else
            {
                Debug.LogError($"Failed to get current rendering sessions. Error: {result.Context.ErrorMessage}");
            }
        }
        catch (RRException ex)
        {
            Debug.LogError($"Failed to get current rendering sessions. Error: {ex.Message}");
        }
        return sessionAvailable;
    }

    /// <summary>
    /// Connects the local runtime to the current active session, if there's a session available
    /// </summary>
    public async void ConnectRuntimeToRemoteSession()
    {
        //Implement me
    }

    public void DisconnectRuntimeFromRemoteSession()
    {
        //Implement me
    }

    /// <summary>
    /// The session must have its runtime pump updated.
    /// The Connection.Update() will push messages to the server, receive messages, and update the frame-buffer with the remotely rendered content.
    /// </summary>
    private void LateUpdate()
    {
        ARRSessionService?.CurrentActiveSession?.Connection?.Update();
    }

    /// <summary>
    /// Loads a model into the remote session for rendering
    /// </summary>
    /// <param name="modelPath">The model's path</param>
    /// <param name="progress">A call back method that accepts a float progress value [0->1]</param>
    /// <param name="parent">The parent Transform for this remote entity</param>
    /// <returns>An awaitable Remote Rendering Entity</returns>
    public async Task<Entity> LoadModel(string modelPath, UnityEngine.Transform parent = null, Action<float> progress = null)
    {
        //Implement me
        return null;
    }

    private async void OnRemoteSessionStatusChanged(ARRServiceUnity caller, RenderingSession session)
    {
        var properties = await session.GetPropertiesAsync();

        switch (properties.SessionProperties.Status)
        {
            case RenderingSessionStatus.Error:
            case RenderingSessionStatus.Expired:
            case RenderingSessionStatus.Stopped:
            case RenderingSessionStatus.Unknown:
                CurrentCoordinatorState = RemoteRenderingState.NoSession;
                break;
            case RenderingSessionStatus.Starting:
                CurrentCoordinatorState = RemoteRenderingState.ConnectingToNewRemoteSession;
                break;
            case RenderingSessionStatus.Ready:
                CurrentCoordinatorState = RemoteRenderingState.RemoteSessionReady;
                break;
        }
    }

    private void OnLocalRuntimeStatusChanged(ConnectionStatus status, Result error)
    {
        switch (status)
        {
            case ConnectionStatus.Connected:
                CurrentCoordinatorState = RemoteRenderingState.RuntimeConnected;
                break;
            case ConnectionStatus.Connecting:
                CurrentCoordinatorState = RemoteRenderingState.ConnectingToRuntime;
                break;
            case ConnectionStatus.Disconnected:
                CurrentCoordinatorState = RemoteRenderingState.RemoteSessionReady;
                break;
        }
    }
}

Créer le GameObject Azure Remote Rendering

Le coordinateur de rendu à distance et son script obligatoire (ARRServiceUnity) sont tous deux des MonoBehaviours qui doivent être attachés à un GameObject de la scène. Le script ARRServiceUnity est fourni par ARR pour exposer la plupart des fonctionnalités d’ARR permettant de se connecter aux sessions à distance et de les gérer.

  1. Créez un GameObject dans la scène (Ctrl+Maj+N ou GameObject->Create Empty) et nommez-le RemoteRenderingCoordinator.
  2. Ajoutez le script RemoteRenderingCoordinator au GameObject RemoteRenderingCoordinator.
    Screenshot of the Unity Add Component dialog. The search text field contains the text RemoteRenderingCoordinator.
  3. Vérifiez que le script ARRServiceUnity, qui apparaît sous le nom Service dans l’inspecteur, est automatiquement ajouté au GameObject. Au cas où vous vous poseriez la question, il faut avoir [RequireComponent(typeof(ARRServiceUnity))] en haut du script RemoteRenderingCoordinator.
  4. Ajoutez vos informations d’identification Azure Remote Rendering, votre domaine de compte et le domaine Remote Rendering dans le script du coordinateur :
    Screenshot of the Unity inspector of the Remote Rendering Coordinator Script. The credential input fields are highlighted.

Initialiser Azure Remote Rendering

Maintenant que nous avons l’infrastructure pour notre coordinateur, nous implémentons chacune des quatre phases en commençant par Initialiser Remote Rendering.

Diagram of the four stages required to load a model. The first stage

L’initialisation indique à Azure Remote Rendering quel objet camera utiliser pour le rendu et fait passer la machine à états à NotAuthorized. Cet état signifie qu’il est initialisé, mais pas encore autorisé à se connecter à une session. Sachant que le démarrage d’une session ARR induit des coûts, nous devons vérifier que l’utilisateur souhaite continuer.

Quand l’état passe à NotAuthorized, CheckAuthorization est appelé, lequel invoque l’événement RequestingAuthorization et détermine quelles informations d’identification de compte utiliser (AccountInfo est défini en haut de la classe et utilise les informations d’identification que vous avez définies dans l’inspecteur Unity à l’étape précédente).

Notes

La recompilation du runtime n’est pas prise en charge par ARR. Si vous modifiez puis enregistrez le script pendant que le mode lecture est actif, Unity peut se figer et vous forcer à l’arrêter par le biais du gestionnaire des tâches. Veillez à toujours arrêter le mode lecture avant de modifier vos scripts.

  1. Remplacez le contenu de InitializeARR et de InitializeSessionService par le code complet ci-dessous :

    /// <summary>
    /// Initializes ARR, associating the main camera
    /// Note: This must be called on the main Unity thread
    /// </summary>
    public void InitializeARR()
    {
        RemoteManagerUnity.InitializeManager(new RemoteUnityClientInit  (Camera.main));
    
        CurrentCoordinatorState = RemoteRenderingState.NotAuthorized;
    }
    
    /// <summary>
    /// Create a new remote session manager
    /// If the ARRCredentialGetter is set, use it as it, otherwise use  the development credentials 
    /// </summary>
    public async void InitializeSessionService()
    {
        if (ARRCredentialGetter == null)
            ARRCredentialGetter = GetDevelopmentCredentials;
    
        var sessionConfiguration = await ARRCredentialGetter.Invoke();
    
        ARRSessionService.OnSessionStatusChanged +=     OnRemoteSessionStatusChanged;
    
        try
        {
            ARRSessionService.Initialize(sessionConfiguration);
        }
        catch (ArgumentException argumentException)
        {
            Debug.LogError(argumentException.Message);
            CurrentCoordinatorState = RemoteRenderingState. NotAuthorized;
            return;
        }
    
        CurrentCoordinatorState = RemoteRenderingState.NoSession;
    }
    

Pour passer de NotAuthorized à NoSession, nous présentons généralement une boîte de dialogue modale à l’utilisateur pour lui permettre de choisir (fait l’objet d’un autre chapitre). Pour le moment, nous contournons automatiquement la vérification d’autorisation en appelant ByPassAuthentication dès que l’événement RequestingAuthorization est déclenché.

  1. Sélectionnez le GameObject RemoteRenderingCoordinator et recherchez l’événement Unity OnRequestingAuthorization exposé dans l’inspecteur du composant RemoteRenderingCoordinator.

  2. Ajoutez un nouvel événement en appuyant sur le signe « + » en bas à droite.

  3. Faites glisser le composant jusqu’à son propre événement pour qu’il se référence lui-même. Screenshot of the Unity inspector of the Remote Rendering Coordinator Script. The title bar of the component is highlighted and an arrow connects it to the On Requesting Authorization event.

  4. Dans la liste déroulante, sélectionnez RemoteRenderingCoordinator -> BypassAuthorization.
    Screenshot of the On Requesting Authorization event.

Créer ou rejoindre une session à distance

La deuxième phase consiste à créer ou rejoindre une session Remote Rendering (pour plus d’informations, consultez Sessions Remote Rendering).

Diagram of the four stages required to load a model. The second stage

C’est dans la session à distance que le rendu des modèles se produit. La méthode JoinRemoteSession( ) tente de rejoindre une session existante, suivie avec la propriété LastUsedSessionID ou s’il y a un ID de session active attribué sur SessionIDOverride. SessionIDOverride est destiné uniquement au débogage. Vous ne devez l’utiliser que si vous savez que la session existe et que vous voulez vous y connecter explicitement.

Si aucune session n’est disponible, une nouvelle session est créée. Cependant, la création d’une session est une opération fastidieuse. Par conséquent, vous ne devez essayer de créer des sessions qu’en cas d’absolue nécessité et les réutiliser dans la mesure du possible (consultez Prêt pour la commercialisation : Regroupement de sessions, planification et bonnes pratiques pour plus d’informations sur la gestion des sessions).

Conseil

StopRemoteSession () met fin à la session active. Pour éviter des frais inutiles, veillez à toujours arrêter les sessions dès que vous n’en n’avez plus besoin.

La machine à états passe maintenant à ConnectingToNewRemoteSession ou ConnectingToExistingRemoteSession, en fonction des sessions disponibles. L’ouverture d’une session existante ou la création d’une session déclenchent toutes deux l’événement ARRSessionService.OnSessionStatusChanged, en exécutant notre méthode OnRemoteSessionStatusChanged. Dans l’idéal, cela a pour effet de définir l’état de la machine à états sur RemoteSessionReady.

  1. Pour rejoindre une nouvelle session, modifiez le code en remplaçant les méthodes JoinRemoteSession( ) et StopRemoteSession( ) par les exemples complets ci-dessous :
/// <summary>
/// Attempts to join an existing session or start a new session
/// </summary>
public async void JoinRemoteSession()
{
    //If there's a session available that previously belonged to us, and it's ready, use it. Otherwise start a new session.
    RenderingSessionProperties joinResult;
    if (await IsSessionAvailable(LastUsedSessionID))
    {
        CurrentCoordinatorState = RemoteRenderingState.ConnectingToExistingRemoteSession;
        joinResult = await ARRSessionService.OpenSession(LastUsedSessionID);
    }
    else
    {
        CurrentCoordinatorState = RemoteRenderingState.ConnectingToNewRemoteSession;
        joinResult = await ARRSessionService.StartSession(new RenderingSessionCreationOptions(renderingSessionVmSize, (int)maxLeaseHours, (int)maxLeaseMinutes));
    }

    if (joinResult.Status == RenderingSessionStatus.Ready || joinResult.Status == RenderingSessionStatus.Starting)
    {
        LastUsedSessionID = joinResult.Id;
    }
    else
    {
        //The session should be ready or starting, if it's not, something went wrong
        await ARRSessionService.StopSession();
        if(LastUsedSessionID == SessionIDOverride)
            SessionIDOverride = "";
        CurrentCoordinatorState = RemoteRenderingState.NoSession;
    }
}

public async void StopRemoteSession()
{
    if (ARRSessionService.CurrentActiveSession != null)
    {
        await ARRSessionService.CurrentActiveSession.StopAsync();
    }
}

Si vous souhaitez gagner du temps en réutilisant des sessions, veillez à désactiver l’option Auto-Stop Session (Arrêter automatiquement la session) dans le composant ARRServiceUnity. Ne perdez pas de vue que les sessions resteront actives, même si personne n’y est connecté. Votre session peut s’exécuter pendant la durée définie dans MaxLeaseTime jusqu’à ce que le serveur l’arrête [la valeur de MaxLeaseTime peut être modifiée dans le composantRemote Rendering Coordinator , en dessous de New Session Defaults (Nouvelles valeurs par défaut de session)]. En revanche, si vous arrêtez automatiquement chaque session pendant la déconnexion, vous devez chaque fois attendre le démarrage d’une nouvelle session, ce qui peut prendre du temps.

Remarque

L’arrêt d’une session prend effet immédiatement et ne peut pas être annulé. Une fois la session arrêtée, vous devez en créer une nouvelle, entraînant les mêmes frais de démarrage.

Connecter le runtime local à la session à distance

L’application doit ensuite connecter son runtime local à la session à distance.

Diagram of the four stages required to load a model. The third stage

L’application doit aussi écouter les événements relatifs à la connexion entre le runtime et la session active ; ces changements d’état sont gérées dans OnLocalRuntimeStatusChanged. Ce code définit l’état sur ConnectingToRuntime. Une fois la connexion établie dans l’état OnLocalRuntimeStatusChanged, l’état devient RuntimeConnected. La connexion au runtime est le dernier état dont se soucie le coordinateur, ce qui signifie que la configuration commune de l’application est terminée et que celle-ci est prête à commencer le travail de session spécifique, à savoir charger les modèles et en assurer le rendu.

  1. Remplacez les méthodes ConnectRuntimeToRemoteSession( ) et DisconnectRuntimeFromRemoteSession( ) par les versions complètes ci-dessous.
  2. Il est important de constater la présence de la méthode Unity LateUpdate et qu’elle met à jour la session active du moment. Cela permet à la session actuelle d’envoyer/recevoir des messages et de mettre à jour la mémoire tampon de frames avec les frames reçues en provenance de la session à distance. Cela est essentiel au bon fonctionnement d’ARR.
/// <summary>
/// Connects the local runtime to the current active session, if there's a session available
/// </summary>
public async void ConnectRuntimeToRemoteSession()
{
    if (ARRSessionService == null || ARRSessionService.CurrentActiveSession == null)
    {
        Debug.LogError("Not ready to connect runtime");
        return;
    }

    // Connect the local runtime to the currently connected session
    // This session is set when connecting to a new or existing session

    ARRSessionService.CurrentActiveSession.ConnectionStatusChanged += OnLocalRuntimeStatusChanged;
    await ARRSessionService.CurrentActiveSession.ConnectAsync(new RendererInitOptions());
}

public void DisconnectRuntimeFromRemoteSession()
{
    if (ARRSessionService == null || ARRSessionService.CurrentActiveSession == null || ARRSessionService.CurrentActiveSession.ConnectionStatus != ConnectionStatus.Connected)
    {
        Debug.LogError("Runtime not connected!");
        return;
    }

    ARRSessionService.CurrentActiveSession.Disconnect();
    ARRSessionService.CurrentActiveSession.ConnectionStatusChanged -= OnLocalRuntimeStatusChanged;
    CurrentCoordinatorState = RemoteRenderingState.RemoteSessionReady;
}

Notes

La connexion du runtime local à une session à distance est tributaire de l’appel de Update dans la session active du moment. Si vous constatez que l’état de votre application ne va jamais au-delà de ConnectingToRuntime, veillez à appeler régulièrement Update au niveau de la session active.

Charger un modèle

Avec la fondation nécessaire en place, vous êtes prêt à charger un modèle dans la session à distance et à commencer à recevoir des images.

Diagram of the four stages required to load a model. The fourth stage

La méthode LoadModel est conçue pour accepter un chemin de modèle, un gestionnaire de progression et une transformation parente. Ces arguments sont utilisés pour charger un modèle dans la session à distance, informer l’utilisateur de la progression du chargement et orienter le modèle rendu à distance sur la transformation parente.

  1. Remplacez entièrement la méthode LoadModel par le code ci-dessous :

    /// <summary>
    /// Loads a model into the remote session for rendering
    /// </summary>
    /// <param name="modelName">The model's path</param>
    /// <param name="parent">The parent Transform for this remote entity</param>
    /// <param name="progress">A call back method that accepts a float progress value [0->1]</param>
    /// <returns>An awaitable Remote Rendering Entity</returns>
    public async Task<Entity> LoadModel(string modelPath, UnityEngine.Transform parent = null, Action<float> progress = null)
    {
        //Create a root object to parent a loaded model to
        var modelEntity = ARRSessionService.CurrentActiveSession.Connection.CreateEntity();
    
        //Get the game object representation of this entity
        var modelGameObject = modelEntity.GetOrCreateGameObject(UnityCreationMode.DoNotCreateUnityComponents);
    
        //Ensure the entity will sync its transform with the server
        var sync = modelGameObject.GetComponent<RemoteEntitySyncObject>();
        sync.SyncEveryFrame = true;
    
        //Parent the new object under the defined parent
        if (parent != null)
        {
            modelGameObject.transform.SetParent(parent, false);
            modelGameObject.name = parent.name + "_Entity";
        }
    
        //Load a model that will be parented to the entity
        var loadModelParams = new LoadModelFromSasOptions(modelPath, modelEntity);
        var loadModelAsync = ARRSessionService.CurrentActiveSession.Connection.LoadModelFromSasAsync(loadModelParams, progress);
        var result = await loadModelAsync;
        return modelEntity;
    }
    

Le code ci-dessus effectue les étapes suivantes :

  1. Création d’une entité distante.
  2. Création d’un GameObject local pour représenter l’entité distante.
  3. Configurez le GameObject local pour synchroniser son état (c.-à-d. Transformation) avec l’entité distante pour chaque image.
  4. Chargement de données de modèle de Stockage Blob vers l’entité distante.
  5. Retour de l’entité parente, pour référence ultérieure.

Afficher le modèle de test

Nous avons maintenant tout le code nécessaire pour afficher un modèle rendu à distance, les quatre phases nécessaires au rendu à distance étant terminées. Il nous faut à présent ajouter un peu de code pour lancer le processus de chargement de modèle.

Diagram of the four stages required to load a model. All stages are marked as completed.

  1. Ajoutez le code suivant à la classe RemoteRenderingCoordinator, par exemple en dessous de la méthode LoadModel :

    private bool loadingTestModel = false;
    [ContextMenu("Load Test Model")]
    public async void LoadTestModel()
    {
        if(CurrentCoordinatorState != RemoteRenderingState.RuntimeConnected)
        {
            Debug.LogError("Please wait for the runtime to connect before loading the test model. Try again later.");
            return;
        }
        if(loadingTestModel)
        {
            Debug.Log("Test model already loading or loaded!");
            return;
        }
        loadingTestModel = true;
    
        // Create a parent object to use for positioning
        GameObject testParent = new GameObject("TestModelParent");
        testParent.transform.position = new Vector3(0f, 0f, 3f);
    
        // The 'built in engine path' is a special path that references a test model built into Azure Remote Rendering.
        await LoadModel("builtin://Engine", testParent.transform, (progressValue) => Debug.Log($"Loading Test Model progress: {Math.Round(progressValue * 100, 2)}%"));
    }
    

    Ce code crée un GameObject pour faire office de parent du modèle de test. Il appelle ensuite la méthode LoadModel pour charger le modèle « builtin://Engine », qui est une ressource intégrée à Azure Remote Rendering destinée à tester le rendu.

  2. Enregistrez votre code.

  3. Appuyez sur le bouton Play (Lecture) dans l’éditeur Unity pour lancer la connexion à Azure Remote Rendering et la création d’une nouvelle session.

  4. Vous ne voyez pas grand-chose dans la vue Jeu, mais la console montre l’évolution de l’état de l’application. Il passera probablement à ConnectingToNewRemoteSession et n’évoluera peut-être plus pendant cinq minutes.

  5. Sélectionnez le GameObject RemoteRenderingCoordinator pour voir les scripts qui lui sont attachés dans l’inspecteur. Observez le composant Service se mettre à jour à mesure qu’il progresse à travers ses étapes d’initialisation et de connexion.

  6. Supervisez la sortie de la console, en attendant que l’état passe à RuntimeConnected.

  7. Une fois le runtime connecté, cliquez avec le bouton droit sur le RemoteRenderingCoordinator dans l’inspecteur pour afficher le menu contextuel. Ensuite, sélectionnez l’option Charger un modèle de test dans le menu contextuel, ajouté par la partie [ContextMenu("Load Test Model")] de notre code ci-dessus.

    Screenshot of the Unity inspector of the Remote Rendering Coordinator Script. Highlights instruct to first right-click on the title bar and then select Load Test Model from the context menu.

  8. Recherchez dans la console la sortie de ProgressHandler que nous avons passée dans la méthode LoadModel.

  9. Examinez le modèle rendu à distance !

Notes

Le modèle distant ne sera jamais visible dans la vue Scene, seulement dans la vue Game. Cela est dû au fait que ARR assure le rendu des frames à distance précisément pour la perspective de la caméra de la vue Game et ne détecte pas la caméra de l’éditeur (utilisée pour le rendu de la vue Scene).

Étapes suivantes

Screenshot of Unity running the project in Play mode. A car engine is rendered in the center of the viewport.

Félicitations ! Vous avez créé une application de base capable d’afficher les modèles rendus à distance utilisant Azure Remote Rendering. Dans le prochain tutoriel, nous intégrons MRTK et importons nos propres modèles.