Samouczek: wyświetlanie modelu renderowanego zdalnie

Ten samouczek zawiera informacje na temat wykonywania następujących czynności:

  • Aprowizuj wystąpienie usługi Azure Remote Rendering (ARR)
  • Tworzenie i zatrzymywanie sesji renderowania
  • Ponowne używanie istniejącej sesji renderowania
  • Nawiązywanie połączenia z sesjami i rozłączanie się z nimi
  • Ładowanie modeli do sesji renderowania

Wymagania wstępne

Na potrzeby tego samouczka potrzebne są następujące elementy:

  • Aktywna subskrypcja platformy Azure z płatnością zgodnie z rzeczywistym użyciem Tworzenie konta
  • Windows SDK 10.0.18362.0 (pobierz)
  • Najnowsza wersja programu Visual Studio 2019 (pobierz)
  • GIT (pobierz)
  • Unity (zobacz wymagania systemowe dotyczące obsługiwanych wersji)
  • Pośrednia wiedza na temat aparatu Unity i języka C# (na przykład: tworzenie skryptów i obiektów, używanie prefab, konfigurowanie zdarzeń aparatu Unity itp.)

Aprowizuj wystąpienie usługi Azure Remote Rendering (ARR)

Aby uzyskać dostęp do usługi Azure Remote Rendering, musisz najpierw utworzyć konto.

Tworzenie nowego projektu aparatu Unity

Porada

Repozytorium przykładów ARR zawiera projekt ze wszystkimi ukończonymi samouczkami. Można go użyć jako odwołania. Poszukaj w aplikacji Unity\Tutorial-Complete , aby zapoznać się z pełnym projektem aparatu Unity.

W usłudze Unity Hub utwórz nowy projekt. W tym przykładzie założono, że projekt jest tworzony w folderze o nazwie RemoteRendering.

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

Uwzględnij pakiety Azure Remote Rendering i OpenXR

Postępuj zgodnie z instrukcjami dotyczącymi dodawania pakietów azure Remote Rendering i OpenXR do projektu aparatu Unity.

Konfigurowanie aparatu

  1. Wybierz węzeł Główny aparat fotograficzny .

  2. Otwórz menu kontekstowe, klikając prawym przyciskiem myszy składnik Przekształć i wybierając opcję Resetuj :

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

  3. Ustaw opcję Wyczyść flagi na kolor stały

  4. Ustaw tło na czarny (#000000), z w pełni przezroczystym (0) alfa (A)

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

  5. Ustaw opcję Przycinanie płaszczyzn na Wartość Blisko = 0,1 i Far = 20. Oznacza to, że renderowanie wyciąć geometrię, która jest bliżej niż 10 cm lub dalej niż 20 metrów.

    Screenshot of the Unity inspector for a Camera component.

Dostosowywanie ustawień projektu

  1. Otwórz pozycję Edytuj > ustawienia projektu...

  2. Wybierz pozycję Jakość z menu listy po lewej stronie

    1. Zmień domyślny poziom jakości wszystkich platform na Niski. To ustawienie umożliwi wydajniejsze renderowanie zawartości lokalnej i nie wpływa na jakość zawartości renderowanej zdalnie.

      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.

  3. Wybierz pozycję Grafika z menu listy po lewej stronie

    1. Zmień ustawienie Skryptowy potok renderowania na HybridRenderingPipeline.
      Screenshot of the Unity Project Settings dialog. The Graphics entry is selected in the list on the left. The button to select a Universal Render Pipeline asset is highlighted.
      Czasami interfejs użytkownika nie wypełnia listy dostępnych typów potoków z pakietów. W takim przypadku zasób HybridRenderingPipeline musi zostać ręcznie przeciągnięty do pola:
      Screenshot of the Unity asset browser and Project Settings dialog. The HybridRenderingPipeline asset is highlighted in the asset browser. An arrow points from the asset to the UniversalRenderPipelineAsset field in project settings.

      Uwaga

      Jeśli nie możesz przeciągać i upuszczać elementu zawartości HybridRenderingPipeline w polu Zasób potoku renderowania (prawdopodobnie dlatego, że pole nie istnieje!), upewnij się, że konfiguracja pakietu zawiera com.unity.render-pipelines.universal pakiet.

  4. Wybierz pozycję Zarządzanie wtyczkami XR z menu listy po lewej stronie

    1. Kliknij przycisk Zainstaluj zarządzanie wtyczkami XR .
    2. Wybierz kartę Ustawienia platformy uniwersalnej systemu Windows reprezentowaną jako ikona systemu Windows.
    3. Kliknij pole wyboru Otwórz XR w obszarze Dostawcy wtyczek
    4. Jeśli zostanie otwarte okno dialogowe z prośbą o włączenie zapleczy platformy natywnej dla nowego systemu wejściowego, kliknij przycisk Nie.

    Screenshot of the Unity Project Settings dialog. The X R 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 X R checkbox below it is also highlighted.

    Uwaga

    Jeśli grupa funkcji Microsoft HoloLens jest wyłączona, w projekcie brakuje wtyczki OpenXR środowiska Windows Mixed Reality. Postępuj zgodnie z instrukcjami dotyczącymi dodawania pakietów Azure Remote Rendering i OpenXR , aby je zainstalować.

  5. Wybierz pozycję OpenXR z menu listy po lewej stronie

    1. Ustaw tryb przesyłania głębokości na głębokość 16-bitowa
    2. Dodaj profil interakcji hand hand firmy Microsoft do profilów interakcji.
    3. Włącz następujące funkcje OpenXR:
      • Śledzenie rąk
      • Funkcje rzeczywistości mieszanej
      • Model kontrolera ruchu

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

    Uwaga

    Jeśli nie widzisz wymaganych funkcji OpenXR na liście brak wtyczki OpenXR środowiska Windows Mixed Reality OpenXR w projekcie. Postępuj zgodnie z instrukcjami dotyczącymi dodawania pakietów Azure Remote Rendering i OpenXR , aby je zainstalować.

  6. Wybierz pozycję Odtwarzacz z menu listy po lewej stronie

    1. Wybierz kartę Ustawienia platformy uniwersalnej systemu Windows reprezentowaną jako ikona systemu Windows.
    2. Rozwiń inne ustawienia
    3. W obszarze Renderowanie zmień przestrzeń kolorów na Liniowa i uruchom ponownie aparat Unity, gdy zostanie wyświetlony monit.
    4. W obszarze Konfiguracja zmień aktywną obsługę danych wejściowych na Zarówno , jak i uruchom ponownie aparat Unity, gdy zostanie wyświetlony monit. 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. Rozwijanie ustawień publikowania
    6. Przewiń w dół do pozycji Możliwości i wybierz pozycję:
      • InternetClient
      • InternetClientServer
      • SpatialPerception
      • PrivateNetworkClientServer (opcjonalnie). Wybierz tę opcję, jeśli chcesz połączyć zdalny debuger aparatu Unity z urządzeniem.
    7. W obszarze Obsługiwane rodziny urządzeń włącz system Holographic and 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.
  7. Zamknij lub zadokuj panel Ustawienia projektu

  8. Otwieranie ustawień kompilacji plików>

    1. Wybieranie platformy uniwersalnej systemu Windows
    2. Skonfiguruj ustawienia, aby były zgodne z poniższymi ustawieniami
    3. Naciśnij przycisk Przełącz 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.
  9. Po zmianie platform przez aparat Unity zamknij panel kompilacji.

Weryfikowanie konfiguracji projektu

Wykonaj następujące kroki, aby sprawdzić, czy ustawienia projektu są poprawne.

  1. Wybierz wpis ValidateProject z menu RemoteRendering na pasku narzędzi edytora aparatu Unity.

  2. Przejrzyj okno ValidateProject pod kątem błędów i w razie potrzeby napraw ustawienia projektu.

    Screenshot of the Unity Validate Project dialog. The dialog shows a mixture of successful checks, warnings, and errors.

Uwaga

Jeśli używasz zestawu narzędzi MRTK w projekcie i włączysz podsystem aparatu, zestaw NARZĘDZI MRTK zastąpi zmiany ręczne, które zostaną zastosowane do aparatu. Obejmuje to poprawki z narzędzia ValidateProject.

Tworzenie skryptu do koordynowania połączenia i stanu usługi Azure Remote Rendering

Istnieją cztery podstawowe etapy wyświetlania zdalnie renderowanych modeli, które opisano w poniższym schemacie blokowym. Każdy etap musi być wykonany w kolejności. Następnym krokiem jest utworzenie skryptu, który będzie zarządzać stanem aplikacji i przechodzić przez każdy wymagany etap.

Diagram of the four stages required to load a model.

  1. W okienku Projekt w obszarze Zasoby utwórz nowy folder o nazwie RemoteRenderingCore. Następnie wewnątrz aplikacji RemoteRenderingCore utwórz inny folder o nazwie Scripts.

  2. Utwórz nowy skrypt języka C# o nazwie RemoteRenderingCoordinator. Projekt powinien wyglądać następująco:

    Screenshot of Unity Project hierarchy containing the new script.

    Ten skrypt koordynacji będzie śledzić stan renderowania zdalnego i zarządzać nim. Należy pamiętać, że niektóre z tych kodów są używane do obsługi stanu, uwidaczniania funkcji innych składników, wyzwalania zdarzeń i przechowywania danych specyficznych dla aplikacji, które nie są bezpośrednio związane z usługą Azure Remote Rendering. Użyj poniższego kodu jako punktu początkowego, a w dalszej części tego samouczka zajmiemy się konkretnym kodem usługi Azure Remote Rendering i zaimplementujemy go.

  3. Otwórz element RemoteRenderingCoordinator w edytorze kodu i zastąp całą jego zawartość poniższym kodem:

// 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 accountId = "<enter your account id here>";
    public string AccountId {
        get => accountId.Trim();
        set => accountId = value;
    }

    [SerializeField]
    private string accountDomain = "<enter your account domain here>";
    public string AccountDomain
    {
        get => accountDomain.Trim();
        set => accountDomain = 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 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 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, 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;
        }
    }
}

Tworzenie obiektu GameObject usługi Azure Remote Rendering

Koordynator renderowania zdalnego i jego wymagany skrypt (ARRServiceUnity) są zarówno MonoBehaviours, które muszą być dołączone do obiektu GameObject w scenie. Skrypt ARRServiceUnity jest dostarczany przez usługę ARR, aby uwidocznić wiele funkcji usługi ARR na potrzeby nawiązywania połączenia z sesjami zdalnymi i zarządzania nimi.

  1. Utwórz nowy obiekt GameObject w scenie (Ctrl+Shift+N lub GameObject-Create> Empty) i nadaj mu nazwę RemoteRenderingCoordinator.
  2. Dodaj skrypt RemoteRenderingCoordinator do obiektu GameObject RemoteRenderingCoordinator .
    Screenshot of the Unity Add Component dialog. The search text field contains the text RemoteRenderingCoordinator.
  3. Upewnij się, że skrypt ARRServiceUnity wyświetlany jako usługa w inspektorze jest automatycznie dodawany do obiektu GameObject. Jeśli zastanawiasz się, jest to wynik [RequireComponent(typeof(ARRServiceUnity))] na początku skryptu RemoteRenderingCoordinator .
  4. Dodaj poświadczenia usługi Azure Remote Rendering, domenę konta i domenę usługi Remote Rendering do skryptu koordynatora:
    Screenshot of the Unity inspector of the Remote Rendering Coordinator Script. The credential input fields are highlighted.

Inicjowanie usługi Azure Remote Rendering

Teraz, gdy mamy już strukturę dla naszego koordynatora, wdrożymy każdy z czterech etapów rozpoczynający się od inicjowania renderowania zdalnego.

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

Inicjowanie informuje usługę Azure Remote Rendering, który obiekt aparatu ma być używany do renderowania i przechodzi maszynę stanu do notAuthorized. Oznacza to, że jest inicjowany, ale nie jest jeszcze autoryzowany do nawiązywania połączenia z sesją. Ponieważ uruchomienie sesji usługi ARR wiąże się z kosztami, musimy potwierdzić, że użytkownik chce kontynuować.

Podczas wprowadzania stanu NotAuthorized wywoływana jest funkcja CheckAuthorization , która wywołuje zdarzenie RequestingAuthorization i określa poświadczenia konta do użycia (Informacje o koncie jest definiowane w górnej części klasy i używa poświadczeń zdefiniowanych za pośrednictwem Inspektora aparatu Unity w powyższym kroku).

Uwaga

Ponowna kompilacja środowiska uruchomieniowego nie jest obsługiwana przez usługę ARR. Modyfikowanie skryptu i zapisywanie go, gdy tryb odtwarzania jest aktywny, może spowodować zamrożenie aparatu Unity i wymusić zamknięcie za pośrednictwem menedżera zadań. Przed rozpoczęciem edytowania skryptów zawsze upewnij się, że tryb odtwarzania został zatrzymany.

  1. Zastąp zawartość elementu InitializeARR i InitializeSessionService poniższym ukończonym kodem:
/// <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)
   {
       NotificationBar.Message("InitializeSessionService failed: SessionConfiguration is invalid.");
       Debug.LogError(argumentException.Message);
       CurrentCoordinatorState = RemoteRenderingState.NotAuthorized;
       return;
   }

   CurrentCoordinatorState = RemoteRenderingState.NoSession;
}

Aby przejść z notAuthorized do NoSession, zazwyczaj przedstawiamy użytkownikowi modalne okno dialogowe, aby mógł wybrać (i zrobimy to tylko w innym rozdziale). Na razie automatycznie pominiemy sprawdzanie autoryzacji, wywołując metodę ByPassAuthentication , gdy tylko zostanie wyzwolone zdarzenie RequestingAuthorization .

  1. Wybierz obiekt GameObject RemoteRenderingCoordinator i znajdź zdarzenie Aparatu Unity OnRequestingAuthorization uwidocznione w inspektorze składnika RemoteRenderingCoordinator .

  2. Dodaj nowe zdarzenie, naciskając klawisz "+" w prawym dolnym rogu.

  3. Przeciągnij składnik na własne zdarzenie, aby się odwołać. 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. Z listy rozwijanej wybierz pozycję RemoteRenderingCoordinator —> BypassAuthorization.
    Screenshot of the On Requesting Authorization event.

Tworzenie lub dołączanie sesji zdalnej

Drugim etapem jest utworzenie lub dołączenie do sesji zdalnego renderowania (zobacz Sesje zdalnego renderowania , aby uzyskać więcej informacji).

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

Sesja zdalna to miejsce renderowania modeli. Metoda JoinRemoteSession( ) podejmie próbę dołączenia istniejącej sesji, śledzonej za pomocą właściwości LastUsedSessionID lub jeśli w identyfikatorze SessionIDOverride jest przypisany aktywny identyfikator sesji. SessionIDOverride jest przeznaczony tylko do celów debugowania, należy go używać tylko wtedy, gdy wiadomo, że sesja istnieje i chcesz jawnie nawiązać z nią połączenie.

Jeśli sesje nie są dostępne, zostanie utworzona nowa sesja. Utworzenie nowej sesji jest jednak czasochłonną operacją. W związku z tym należy próbować tworzyć sesje tylko wtedy, gdy jest to wymagane, i używać ich ponownie zawsze, gdy jest to możliwe (zobacz Commercial Ready: Session pooling, scheduling, and best practices for more information on managing sessions( Komercyjna gotowość: buforowanie sesji, planowanie i najlepsze rozwiązania , aby uzyskać więcej informacji na temat zarządzania sesjami).

Porada

Polecenie StopRemoteSession() zakończy aktywną sesję. Aby zapobiec niepotrzebnym opłatom, należy zawsze zatrzymywać sesje, gdy nie są już potrzebne.

Maszyna stanu będzie teraz przechodzić do stanu ConnectingToNewRemoteSession lub ConnectingToExistingRemoteSession, w zależności od dostępnych sesji. Zarówno otwarcie istniejącej sesji, jak i utworzenie nowej sesji spowoduje wyzwolenie zdarzenia ARRSessionService.OnSessionStatusChanged , wykonując naszą metodę OnRemoteSessionStatusChanged . W idealnym przypadku spowoduje to przejście maszyny stanu do elementu RemoteSessionReady.

  1. Aby dołączyć do nowej sesji, zmodyfikuj kod, aby zastąpić metody JoinRemoteSession( ) i StopRemoteSession( ) za pomocą ukończonych przykładów poniżej:
/// <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 void StopRemoteSession()
{
    if (ARRSessionService.CurrentActiveSession != null)
    {
        ARRSessionService.CurrentActiveSession.StopAsync();
    }
}

Jeśli chcesz zaoszczędzić czas, ponownie używając sesji, pamiętaj, aby dezaktywować opcję Sesja automatycznego zatrzymywania w składniku ARRServiceUnity . Należy pamiętać, że spowoduje to pozostawienie uruchomionych sesji nawet wtedy, gdy nikt nie jest z nimi połączony. Sesja może działać tak długo, jak MaxLeaseTime , zanim zostanie ona zamknięta przez serwer (wartość MaxLeaseTime można zmodyfikować w Koordynatorze renderowania zdalnego w obszarze Ustawienia domyślne nowej sesji). Z drugiej strony, jeśli automatycznie zamkniesz każdą sesję podczas rozłączenia, za każdym razem trzeba będzie poczekać na rozpoczęcie nowej sesji, co może być nieco długotrwałym procesem.

Uwaga

Zatrzymanie sesji zacznie obowiązywać natychmiast i nie można jej cofnąć. Po zatrzymaniu należy utworzyć nową sesję z tym samym obciążeniem uruchamiania.

Łączenie lokalnego środowiska uruchomieniowego z sesją zdalną

Następnie aplikacja musi połączyć lokalne środowisko uruchomieniowe z sesją zdalną.

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

Aplikacja musi również nasłuchiwać zdarzeń dotyczących połączenia między środowiskiem uruchomieniowym a bieżącą sesją; te zmiany stanu są obsługiwane w onLocalRuntimeStatusChanged. Ten kod spowoduje przejście do stanu ConnectingToRuntime. Po nawiązaniu połączenia w elemencie OnLocalRuntimeStatusChanged stan zmieni się na RuntimeConnected. Nawiązywanie połączenia ze środowiskiem uruchomieniowym jest ostatnim stanem, z którym sam jest związane koordynator, co oznacza, że aplikacja jest wykonywana z całą wspólną konfiguracją i jest gotowa do rozpoczęcia pracy specyficznej dla sesji podczas ładowania i renderowania modeli.

  1. Zastąp metody ConnectRuntimeToRemoteSession( ) i DisconnectRuntimeFromRemoteSession( ) ukończonymi wersjami poniżej.
  2. Należy pamiętać o metodzie Aparatu Unity LateUpdate i zaktualizowaniu bieżącej aktywnej sesji. Dzięki temu bieżąca sesja może wysyłać/odbierać komunikaty i aktualizować bufor ramki ramką odebraną z sesji zdalnej. Ważne jest, aby ARR działało poprawnie.
/// <summary>
/// Connects the local runtime to the current active session, if there's a session available
/// </summary>
public 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;
    ARRSessionService.CurrentActiveSession.ConnectAsync(new RendererInitOptions());
    CurrentCoordinatorState = RemoteRenderingState.ConnectingToRuntime;
}

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

Uwaga

Połączenie lokalnego środowiska uruchomieniowego z sesją zdalną zależy od wywoływanej aktualizacji w aktualnie aktywnej sesji. Jeśli okaże się, że aplikacja nigdy nie przechodzi poza stan ConnectingToRuntime , upewnij się, że regularnie wywołujesz funkcję Update w aktywnej sesji.

Ładowanie modelu

Po utworzeniu wymaganej podstawy możesz załadować model do sesji zdalnej i rozpocząć odbieranie ramek.

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

Metoda LoadModel została zaprojektowana tak, aby akceptowała ścieżkę modelu, procedurę obsługi postępu i transformację nadrzędną. Te argumenty będą używane do ładowania modelu do sesji zdalnej, aktualizowania użytkownika na postęp ładowania i orientowania zdalnie renderowanego modelu na podstawie przekształcenia nadrzędnego.

  1. Zastąp metodę LoadModel całkowicie poniższym kodem:

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

Powyższy kod wykonuje następujące kroki:

  1. Utwórz jednostkę zdalną.
  2. Utwórz lokalny obiekt GameObject reprezentujący jednostkę zdalną.
  3. Skonfiguruj lokalny obiekt GameObject, aby zsynchronizować jego stan (tj. Przekształć) z jednostką zdalną w każdej ramce.
  4. Ładowanie danych modelu z usługi Blob Storage do jednostki zdalnej.
  5. Zwróć jednostkę nadrzędną w celu późniejszego odwołania.

Wyświetlanie modelu testowego

Mamy teraz cały kod wymagany do wyświetlenia zdalnie renderowanego modelu. Wszystkie cztery etapy wymagane do renderowania zdalnego zostały ukończone. Teraz musimy dodać trochę kodu, aby rozpocząć proces ładowania modelu.

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

  1. Dodaj następujący kod do klasy RemoteRenderingCoordinator , tuż poniżej metody 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)}%"));
    }
    

    Ten kod tworzy obiekt GameObject do działania jako element nadrzędny modelu testowego. Następnie wywołuje metodę LoadModel , aby załadować model "builtin://Engine", który jest zasobem wbudowanym w usługę Azure Remote Rendering na potrzeby testowania renderowania.

  2. Zapisz kod.

  3. Naciśnij przycisk Odtwórz w edytorze aparatu Unity, aby rozpocząć proces nawiązywania połączenia z usługą Azure Remote Rendering i tworzenia nowej sesji.

  4. W widoku Game (Gra) nie będzie widocznych zbyt wiele, jednak konsola pokaże stan zmiany aplikacji. Prawdopodobnie przejdzie ConnectingToNewRemoteSessiondo , i pozostanie tam, prawdopodobnie przez maksymalnie pięć minut.

  5. Wybierz obiekt GameObject RemoteRenderingCoordinator , aby wyświetlić dołączone skrypty w inspektorze. Obserwuj aktualizację składnika usługi w miarę postępu procesu inicjowania i kroków połączenia.

  6. Monitoruj dane wyjściowe konsoli — oczekiwanie na zmianę stanu na RuntimeConnected.

  7. Po nawiązaniu połączenia środowiska uruchomieniowego kliknij prawym przyciskiem myszy element RemoteRenderingCoordinator w inspektorze, aby uwidocznić menu kontekstowe. Następnie kliknij opcję Model testu obciążeniowego w menu kontekstowym dodanym [ContextMenu("Load Test Model")] przez część naszego kodu powyżej.

    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. Obejrzyj konsolę danych wyjściowych metody ProgressHandler , która została przekazana do metody LoadModel .

  9. Zobacz zdalnie renderowany model!

Uwaga

Model zdalny nigdy nie będzie widoczny w widoku Scena, tylko w widoku gry. Wynika to z tego, że usługa ARR zdalnie renderuje ramki specjalnie dla perspektywy kamery Widok gry i nie jest świadoma aparatu Edytora (używanego do renderowania widoku sceny).

Następne kroki

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

Gratulacje! Utworzono podstawową aplikację, która może wyświetlać zdalnie renderowane modele przy użyciu usługi Azure Remote Rendering. W następnym samouczku zintegrujemy zestaw narzędzi MRTK i zaimportujemy własne modele.