Tutorial: Anzeigen eines per Remotezugriff gerenderten Modells

In diesem Tutorial lernen Sie Folgendes:

  • Bereitstellen einer Azure Remote Rendering-Instanz (ARR)
  • Erstellen und Beenden einer Renderingsitzung
  • Erneutes Verwenden einer vorhandenen Renderingsitzung
  • Herstellen und Trennen von Verbindungen mit Sitzungen
  • Laden von Modellen in eine Renderingsitzung

Voraussetzungen

Für dieses Tutorial benötigen Sie Folgendes:

  • Ein aktives Azure-Abonnement mit nutzungsbasierter Bezahlung, Konto erstellen
  • Windows SDK 10.0.18362.0 (herunterladen)
  • Die aktuellste Version von Visual Studio 2022 (Download)
  • Git (herunterladen)
  • Git-LFS-Plugin (herunterladen)
  • Unity (Informationen zu den unterstützten Versionen finden Sie unter Systemanforderungen.)
  • Grundkenntnisse von Unity und der Sprache C# (z. B. Erstellen von Skripts und Objekten, Verwenden von Prefabs, Konfigurieren von Unity-Ereignissen usw.)

Bereitstellen einer Azure Remote Rendering-Instanz (ARR)

Sie müssen zunächst ein Konto erstellen, um Zugriff auf Azure Remote Rendering zu erhalten.

Erstellen eines neuen Unity-Projekts

Tipp

Das ARR-Beispielrepository enthält ein Projekt, in dem alle Tutorials abgeschlossen sind, dieses kann als Referenz verwendet werden. Suchen Sie unter Unity\Tutorial-Complete nach dem gesamten Unity-Projekt.

Erstellen Sie im Unity Hub ein neues Projekt. Bei diesem Beispiel gehen wir davon aus, dass das Projekt in einem Ordner namens RemoteRendering erstellt wird.

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

Einbeziehen des Azure Remote Rendering- und OpenXR-Pakets

Befolgen Sie die Anweisungen zum Hinzufügen der Azure Remote Rendering- und OpenXR-Pakete zu Ihrer Unity-Project.

Hinweis

Wenn Unity nach dem Importieren des OpenXR-Pakets ein Warnungsdialogfeld anzeigt, in dem gefragt wird, ob die nativen Plattform-Backends für das neue Eingabesystem aktiviert werden sollen, klicken Sie vorerst auf Nein. Sie werden diese Option ihn in einem späteren Schritt aktivieren.

Konfigurieren der Kamera

  1. Wählen Sie den Knoten Main Camera (Hauptkamera) aus.

  2. Öffnen Sie das Kontextmenü, indem Sie mit der rechten Maustaste auf die Komponente Transform (Transformation) klicken und die Option Reset (Zurücksetzen) auswählen:

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

  3. Legen Sie Clear flags (Kennzeichnungen löschen) auf Solid Color (Volltonfarbe) fest.

  4. Legen Sie Background (Hintergrund) auf Black (Schwarz) (#000000) mit vollständig transparent (0) Alpha (A) fest.

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

  5. Legen Sie Clipping Planes (Clippingebenen) auf Near (Nahe) = 0,1 und Far (Entfernt) = 20 fest. Das bedeutet, dass beim Rendern Geometrien beschnitten werden, die näher als 10 cm oder weiter als 20 Meter entfernt sind.

    Screenshot of the Unity inspector for a Camera component.

Anpassen der Projekteinstellungen

  1. Öffnen Sie Edit > Project Settings... (Bearbeiten > Projekteinstellungen).

  2. Wählen Sie im Menü auf der linken Seite Quality (Qualität) aus.

    1. Ändern Sie Default Quality Level (Standardqualitätsstufe) für alle Plattformen in Low (Niedrig). Diese Einstellung ermöglicht eine effizientere Darstellung von lokalem Inhalt und wirkt sich nicht auf die Qualität von per Remotezugriff gerendertem Inhalt aus.

      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.

    Hinweis

    In diesem Tutorial bleiben wir bei der integrierten Unity-Renderpipeline. Wenn Sie die Universal Render Pipeline verwenden möchten, finden Sie unter Unity-Renderpipelines weitere Einrichtungsschritte.

  3. Wählen Sie im linken Listenmenü die Option XR Plugin Management (Verwaltung von XR-Plug-Ins) aus.

    1. Klicken Sie auf die Schaltfläche Install XR Plugin Management (XR-Plug-In-Verwaltung installieren).
    2. Wählen Sie die Registerkarte Universal Windows Platform settings (UWP-Einstellungen) aus, die als Windows-Symbol dargestellt ist.
    3. Aktivieren Sie unter Plug-In Providers (Plug-In-Anbieter) das Kontrollkästchen OpenXR
    4. Wenn ein Dialogfeld geöffnet wird, in dem Sie aufgefordert werden, die nativen Plattform-Back-Ends für das neue Eingabesystem zu aktivieren, wählen Sie No (Nein).

    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.

    Hinweis

    Wenn die Microsoft HoloLens-Featuregruppe deaktiviert ist, ist das Windows Mixed Reality-OpenXR-Plug-In in Ihrem Projekt nicht vorhanden. Befolgen Sie die Anweisungen zum Hinzufügen der Azure Remote Rendering- und OpenXR-Pakete, um diese zu installieren.

  4. Wählen Sie im Menü auf der linken Seite OpenXR aus.

    1. Legen Sie Depth Submission Mode (Tiefenübermittlungsmodus) auf Depth 16 Bit (Tiefe 16 Bit) fest.
    2. Fügen Sie Microsoft Hand Interaction Profil (Microsoft-Handinteraktionsprofil) zu Interaction Profiles (Interaktionsprofile) hinzu.
    3. Aktivieren Sie diese OpenXR-Features:
      • Azure Remote Rendering
      • Hand-Tracking
      • Mixed Reality-Features
      • Motion Controller-Modell

    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.

    Hinweis

    Wenn die erforderlichen OpenXR-Features nicht aufgeführt sind, fehlt das Windows Mixed Reality-OpenXR-Plug-In in Ihrem Projekt. Befolgen Sie die Anweisungen zum Hinzufügen der Azure Remote Rendering- und OpenXR-Pakete, um diese zu installieren.

  5. Wählen Sie im Menü auf der linken Seite Player aus.

    1. Wählen Sie die Registerkarte Universal Windows Platform settings (UWP-Einstellungen) aus, die als Windows-Symbol dargestellt ist.
    2. Erweitern Sie Other Settings (Weitere Einstellungen).
    3. Ändern Sie unter Rendering den Color Space (Farbraum) in Linear, und starten Sie Unity neu, wenn Sie dazu aufgefordert werden.
    4. Ändern Sie unter Configuration (Konfiguration) die Option Active Input Handling(Aktive Eingabebehandlung) in Both (Beide), und starten Sie Unity neu, wenn Sie dazu aufgefordert werden. 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. Erweitern Sie Publishing Settings (Veröffentlichungseinstellungen).
    6. Scrollen Sie nach unten zu Capabilities (Funktionen), und wählen Sie Folgendes aus:
      • InternetClient
      • InternetClientServer
      • SpatialPerception
      • PrivateNetworkClientServer (optional). Wählen Sie diese Option aus, wenn Sie den Unity-Remotedebugger mit Ihrem Gerät verbinden möchten.
    7. Aktivieren Sie im Dialogfeld „Unity-Projekteinstellungen“ unter Unterstützte Gerätefamilien die Optionen Holografie und 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. Schließen Sie das Panel Project Settings (Projekteinstellungen), oder docken Sie es an.

  7. Öffnen Sie File > Build Settings (Datei > Buildeinstellungen).

    1. Wählen Sie Universal Windows Platform (Universelle Windows-Plattform) aus.
    2. Konfigurieren Sie die Einstellungen so, dass Sie den unten aufgeführten entsprechen.
    3. Klicken Sie auf die Schaltfläche Switch Platform (Plattform wechseln).
      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. Nachdem Unity die Plattformen geändert hat, schließen Sie das Buildpanel.

Überprüfen der Projekteinrichtung

Führen Sie die folgenden Schritte aus, um zu überprüfen, ob die Projekteinstellungen stimmen.

  1. Wählen Sie auf der Symbolleiste des Unity-Editors im Menü RemoteRendering den Eintrag ValidateProject aus.

  2. Überprüfen Sie das Fenster Projektvalidierung auf Fehler und korrigieren Sie ggf. die Projekteinstellungen.

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

Hinweis

Wenn Sie MRTK in Ihrem Projekt verwenden und das Kamerasubsystem aktivieren, überschreibt MRTK manuelle Änderungen, die Sie auf die Kamera anwenden. Dazu gehören Fixes aus dem ValidateProject-Tool.

Erstellen eines Skripts zum Koordinieren der Azure Remote Rendering-Verbindung und des Zustands

Es gibt vier grundlegende Phasen zum Anzeigen von per Remotezugriff gerenderten Modellen, die im Flussdiagramm unten beschrieben werden. Jede Phase muss in der richtigen Reihenfolge ausgeführt werden. Der nächste Schritt besteht darin, ein Skript zu erstellen, das den Anwendungszustand verwaltet und die einzelnen erforderlichen Phasen durchläuft.

Diagram of the four stages required to load a model.

  1. Erstellen Sie im Bereich Project (Projekt) unter Assets (Objekte) einen neuen Ordner mit dem Namen RemoteRenderingCore. Erstellen Sie anschließend in RemoteRenderingCoreeinen weiteren Ordner mit dem Namen Scripts (Skripts).

  2. Erstellen Sie ein neues C#-Skript mit dem Namen RemoteRenderingCoordinator. Ihr Projekt sollte wie folgt aussehen:

    Screenshot of Unity Project hierarchy containing the new script.

    Dieses Koordinatorskript verfolgt und verwaltet den Remoterenderingstatus. Beachten Sie, dass ein Teil dieses Codes für die Aufrechterhaltung des Zustands, das Verfügbarmachen von Funktionen für andere Komponenten, das Auslösen von Ereignissen und das Speichern anwendungsspezifischer Daten verwendet wird, die nicht direkt mit Azure Remote Rendering in Verbindung stehen. Verwenden Sie den folgenden Code als Ausgangspunkt, und wir behandeln und implementieren den spezifischen Azure Remote Rendering-Code später in diesem Tutorial.

  3. Öffnen Sie RemoteRenderingCoordinator im Code-Editor, und ersetzen Sie den gesamten Inhalt durch den folgenden Code:

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

Erstellen Des Azure Remote Rendering-GameObject-Elements

Der Remote Rendering-Koordinator und das erforderliche Skript (ARRServiceUnity) sind beide MonoBehaviours-Elemente, die an ein GameObject-Element in der Szene angefügt werden müssen. Das Skript ARRServiceUnity wird von ARR bereitgestellt, um die Funktionen von ARR verfügbar zu machen, mit denen eine Verbindung mit Remotesitzungen hergestellt wird und diese verwaltet werden.

  1. Erstellen Sie ein neues GameObject-Element in der Szene (STRG + UMSCHALT + N oder GameObject > Creaty Empty (Leer erstellen)), und nennen Sie es RemoteRenderingCoordinator.
  2. Fügen Sie das Skript RemoteRenderingCoordinator zum GameObject-Element RemoteRenderingCoordinator hinzu.
    Screenshot of the Unity Add Component dialog. The search text field contains the text RemoteRenderingCoordinator.
  3. Überprüfen Sie, ob das Skript ARRServiceUnity, das als Service (Dienst) im Inspektor angezeigt wird, automatisch zum GameObject-Element hinzugefügt wird. Wenn Sie sich unsicher sind, ist dies ein Ergebnis mit [RequireComponent(typeof(ARRServiceUnity))] am Anfang des RemoteRenderingCoordinator-Skripts.
  4. Fügen Sie Ihre Azure Remote Rendering-Anmeldeinformationen, Ihre Kontodomäne und die Remote Rendering-Domäne zum Koordinatorskript hinzu:
    Screenshot of the Unity inspector of the Remote Rendering Coordinator Script. The credential input fields are highlighted.

Initialisieren von Azure Remote Rendering

Nachdem wir nun über das Framework für den Koordinator verfügen, implementieren wir jede der vier Phasen, beginnend mit dem Initialisieren von Remote Rendering.

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

Initialisieren weist Azure Remote Rendering an, welches Kameraobjekt zum Rendern verwendet wird, und versetzt den Zustandsautomaten in NotAuthorized. Dies bedeutet, dass es initialisiert wurde, aber noch nicht autorisiert ist, eine Verbindung mit einer Sitzung herzustellen. Da das Starten einer ARR-Sitzung Kosten verursacht, muss bestätigt werden, dass der Benutzer den Vorgang fortsetzen möchte.

Beim Erreichen des Zustands NotAuthorized wird CheckAuthorization aufgerufen, wodurch das Ereignis RequestingAuthorization aufgerufen und festgelegt wird, welche Kontoanmeldeinformationen verwendet werden sollen (AccountInfo wird in der Nähe des oberen Rands der Klasse definiert und verwendet die Anmeldeinformationen, die Sie im obigen Schritt über den Unity Inspector definiert haben).

Hinweis

Die Neukompilierung der Runtime wird von ARR nicht unterstützt. Wenn Sie das Skript ändern und speichern, solange der Wiedergabemodus aktiv ist, kann es zum Einfrieren von Unity kommen. Sie sind dann gezwungen, das Skript über den Task-Manager herunterzufahren. Stellen Sie immer sicher, dass Sie den Wiedergabemodus beendet haben, bevor Sie Ihre Skripts bearbeiten.

  1. Ersetzen Sie den Inhalt von InitializeARR und InitializeSessionService durch den folgenden abgeschlossenen Code:

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

Um von NotAuthorized zu NoSessionzu gelangen, stellen wir den Benutzern normalerweise ein modales Dialogfeld zur Verfügung, in denen sie eine Auswahl treffen können (das behandeln wir in einem anderen Kapitel). Derzeit umgehen wir die Autorisierungsüberprüfung durch Aufrufen von ByPassAuthentication, sobald das Ereignis RequestingAuthorization ausgelöst wird.

  1. Wählen Sie das GameObject-Element RemoteRenderingCoordinator aus, und suchen Sie nach dem Unity-Ereignis OnRequestingAuthorization, das im Inspektor der Komponente RemoteRenderingCoordinator verfügbar gemacht wird.

  2. Fügen Sie ein neues Ereignis hinzu, indem Sie in der unteren rechten Ecke auf „+“ klicken.

  3. Ziehen Sie die Komponente auf ihr eigenes Ereignis, damit sie auf sich selbst verweist. 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. Wählen Sie in der Dropdownliste RemoteRenderingCoordinator > BypassAuthorization> aus.
    Screenshot of the On Requesting Authorization event.

Erstellen einer Remotesitzung oder Teilnehmen

Die zweite Phase besteht darin, eine Remoterenderingsitzung zu erstellen oder an einer Remoterenderingsitzung teilzunehmen (weitere Informationen zu Renderingsitzungen finden Sie unter Remoterenderingsitzungen).

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

In der Remotesitzung werden die Modelle gerendert. Die Methode JoinRemoteSession( ) versucht, an einer vorhandenen Sitzung teilzunehmen, die mit der Eigenschaft LastUsedSessionID nachverfolgt wird, oder – wenn eine aktive Sitzungs-ID zugewiesen ist – mit SessionIDOverride. SessionIDOverride ist nur für Debuggingzwecke gedacht und sollte nur verwendet werden, wenn Sie wissen, dass die Sitzung vorhanden ist und Sie explizit eine Verbindung mit ihr herstellen möchten.

Wenn keine Sitzungen verfügbar sind, wird eine neue Sitzung erstellt. Das Erstellen einer neuen Sitzung ist allerdings ein zeitaufwendiger Vorgang. Daher sollten Sie versuchen, nur dann Sitzungen zu erstellen, wenn dies erforderlich ist, und sie nach Möglichkeit wiederzuverwenden (weitere Informationen zum Verwalten von Sitzungen finden Sie unter Anwendungen für gewerbliche Zwecke: Sitzungspools, Planung und bewährte Methoden).

Tipp

StopRemoteSession() beendet die aktive Sitzung. Um nicht erforderliche Kosten zu vermeiden, sollten Sie Sitzungen immer dann beenden, wenn sie nicht mehr benötigt werden.

Der Zustandsautomat wechselt nun in Abhängigkeit von den verfügbaren Sitzungen entweder zu ConnectingToNewRemoteSession oder ConnectingToNewRemoteSession. Sowohl das Öffnen einer vorhandenen Sitzung als auch das Erstellen einer neuen Sitzung löst das Ereignis ARRSessionService.OnSessionStatusChanged aus, wobei die Methode OnRemoteSessionStatusChanged ausgeführt wird. Im Idealfall führt dies dazu, dass der Zustandsautomat in RemoteSessionReady verschoben wird.

  1. Um einer neuen Sitzung beizutreten, ändern Sie den Code so, dass die Methoden JoinRemoteSession( ) und StopRemoteSession( ) durch die folgenden abgeschlossenen Beispiele ersetzt werden:
/// <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();
    }
}

Wenn Sie Zeit sparen möchten, indem Sie Sitzungen wiederverwenden, stellen Sie sicher, dass Sie die Option Auto-Stop Session (Sitzung automatisch beenden) in der Komponente ARRServiceUnity deaktivieren. Beachten Sie, dass dadurch die Sitzungen weiterhin ausgeführt werden, auch wenn keine Verbindung mit Ihnen besteht. Ihre Sitzung kann so lange ausgeführt werden, wie unter MaxLeaseTime festgelegt, bevor sie vom Server heruntergefahren wird (der Wert für MaxLeaseTime kann im Remote Rendering-Koordinator unter New Session Defaults (Neue Sitzungsstandardwerte) geändert werden). Wenn Sie hingegen jede Sitzung automatisch herunterfahren, wenn die Verbindung getrennt wird, müssen Sie jedes Mal warten, bis eine neue Sitzung gestartet wird. Dies kann ein langwieriger Prozess sein.

Hinweis

Das Beenden einer Sitzung erfolgt sofort und kann nicht rückgängig gemacht werden. Einmal beendet, müssen Sie eine neue Sitzung mit dem gleichen Startaufwand erstellen.

Herstellen einer Verbindung zwischen der lokalen Runtime und der Remotesitzung

Als Nächstes muss die Anwendung eine Verbindung zwischen der lokalen Runtime und der Remotesitzung herstellen.

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

Die Anwendung muss auch auf Ereignisse über die Verbindung zwischen der Runtime und der aktuellen Sitzung lauschen. Diese Zustandsänderungen werden in OnLocalRuntimeStatusChanged behandelt. Mit diesem Code wird der Zustand in ConnectingToRuntime geändert. Sobald die Verbindung in OnLocalRuntimeStatusChanged hergestellt wurde, wechselt der Zustand zu RuntimeConnected. Das Herstellen einer Verbindung mit der Runtime ist der letzte Zustand, mit dem sich der Koordinator selbst beschäftigt. Das bedeutet, dass die Anwendung mit der gesamten allgemeinen Konfiguration erfolgt und bereit ist, die sitzungsspezifischen Aufgaben zum Laden und Rendern von Modellen zu beginnen.

  1. Ersetzen Sie die Methoden ConnectRuntimeToRemoteSession( ) und DisconnectRuntimeFromRemoteSession( ) durch die folgenden abgeschlossenen Versionen.
  2. Es ist wichtig, die Unity-Methode LateUpdate zu notieren und die aktuelle aktive Sitzung zu aktualisieren. Dies ermöglicht der aktuellen Sitzung das Senden/Empfangen von Nachrichten und das Aktualisieren des Framepuffers mit den Frames, die von der Remotesitzung empfangen werden. Es ist wichtig, dass ARR ordnungsgemäß funktioniert.
/// <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;
}

Hinweis

Das Herstellen einer Verbindung zwischen der lokalen Runtime und einer Remotesitzung hängt davon ab, dass Update (Aktualisieren) regelmäßig für die derzeit aktive Sitzung aufgerufen wird. Wenn Sie feststellen, dass Ihre Anwendung nie über den Zustand ConnectingToRuntime hinausgeht, stellen Sie sicher, dass Sie Update (Aktualisieren) regelmäßig in der aktiven Sitzung aufrufen.

Laden eines Modells

Wenn die erforderliche Grundlage erfüllt ist, können Sie ein Modell in die Remotesitzung laden und mit dem Empfangen von Frames beginnen.

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

Die Methode LoadModel ist so konzipiert, dass ein Modellpfad, ein Fortschrittshandler und eine übergeordnete Transformation akzeptiert werden. Diese Argumente werden verwendet, um ein Modell in die Remotesitzung zu laden, die Benutzer über den Ladefortschritt zu informieren und das per Remotezugriff gerenderte Modell basierend auf der übergeordneten Transformation auszurichten.

  1. Ersetzen Sie die Methode LoadModel vollständig durch den folgenden Code:

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

Dieser Code führt die folgenden Schritte aus:

  1. Erstellen Sie eine Remoteentität.
  2. Erstellen Sie ein lokales GameObject-Element, das die Remoteentität darstellt.
  3. Konfigurieren Sie das lokale GameObject-Element so, dass es seinen Zustand (d. h. Transformation) für jedes Frame mit der Remoteentität synchronisiert.
  4. Laden von Modelldaten aus Blob Storage in die Remoteentität.
  5. Geben Sie die übergeordnete Entität zum späteren Verweis zurück.

Anzeigen des Testmodells

Wir verfügen jetzt über den gesamten Code, der zum Anzeigen eines per Remotezugriff gerenderten Modells erforderlich ist. Alle vier für das Remote Rendering erforderlichen Phasen sind vollständig. Nun müssen wir ein wenig Code hinzufügen, um den Ladeprozess für das Modell zu starten.

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

  1. Fügen Sie den folgenden Code zur Klasse RemoteRenderingCoordinator hinzu, am besten direkt unterhalb der Methode 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)}%"));
    }
    

    Mit diesem Code wird ein GameObject-Element erstellt, das als übergeordnetes Element für das Testmodell fungiert. Anschließend wird die LoadModel-Methode aufgerufen, um das Modell „builtin://Engine“ zu laden. Dabei handelt es sich um ein Objekt, das in Azure Remote Rendering integriert ist, um das Rendering zu testen.

  2. Speichern Sie Ihren Code.

  3. Betätigen Sie im Unity-Editor die Wiedergabeschaltfläche, um das Herstellen einer Verbindung mit Azure Remote Rendering zu starten und eine neue Sitzung zu erstellen.

  4. In der Spielansicht wird nicht viel angezeigt, aber in der Konsole sehen Sie, wie sich der Zustand der Anwendung ändert. Er ändert sich wahrscheinlich in ConnectingToNewRemoteSession, was sich möglicherweise bis zu fünf Minuten nicht mehr ändert.

  5. Wählen Sie das GameObject-Element RemoteRenderingCoordinator aus, um die angefügten Skripts im Inspektor anzuzeigen. Sie sehen, wie die Komponente Service (Dienst) aktualisiert wird, während die Initialisierung und die Verbindungsschritte durchlaufen werden.

  6. Überwachen Sie die Konsolenausgabe, und warten Sie darauf, dass sich der Zustand in RuntimeConnected ändert.

  7. Sobald die Verbindung der Runtime hergestellt wurde, klicken Sie mit der rechten Maustaste auf RemoteRenderingCoordinator im Inspektor, um das Kontextmenü anzuzeigen. Klicken Sie anschließend im Kontextmenü auf die Option Load Test Model (Testmodell laden), die vom Teil [ContextMenu("Load Test Model")] des oben angegebenen Code hinzugefügt wurde.

    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. Achten Sie in der Konsole auf die Ausgabe von ProgressHandler, die in die Methode LoadModel übergeben wurde.

  9. Zeigen Sie das per Remotezugriff gerenderte Modell an.

Hinweis

Das Remotemodell wird in der Szenenansicht niemals angezeigt, nur in der Spielansicht. Dies liegt daran, dass ARR die Frames per Remotezugriff speziell für die Perspektive der Spielansichtskamera rendert und die Editorkamera nicht kennt (wird zum Rendern der Szenenansicht verwendet).

Nächste Schritte

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

Herzlichen Glückwunsch! Sie haben eine einfache Anwendung erstellt, mit der per Remotezugriff gerenderte Modelle mithilfe von Azure Remote Rendering angezeigt werden können. Im nächsten Tutorial integrieren wir MRTK und importieren unsere eigenen Modelle.