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.
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
Wybierz węzeł Główny aparat fotograficzny .
Otwórz menu kontekstowe, klikając prawym przyciskiem myszy składnik Przekształć i wybierając opcję Resetuj :
Ustaw opcję Wyczyść flagi na kolor stały
Ustaw tło na czarny (#000000), z w pełni przezroczystym (0) alfa (A)
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.
Dostosowywanie ustawień projektu
Otwórz pozycję Edytuj > ustawienia projektu...
Wybierz pozycję Jakość z menu listy po lewej stronie
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.
Wybierz pozycję Grafika z menu listy po lewej stronie
Zmień ustawienie Skryptowy potok renderowania na HybridRenderingPipeline.
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:
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.
Wybierz pozycję Zarządzanie wtyczkami XR z menu listy po lewej stronie
- Kliknij przycisk Zainstaluj zarządzanie wtyczkami XR .
- Wybierz kartę Ustawienia platformy uniwersalnej systemu Windows reprezentowaną jako ikona systemu Windows.
- Kliknij pole wyboru Otwórz XR w obszarze Dostawcy wtyczek
- Jeśli zostanie otwarte okno dialogowe z prośbą o włączenie zapleczy platformy natywnej dla nowego systemu wejściowego, kliknij przycisk Nie.
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ć.
Wybierz pozycję OpenXR z menu listy po lewej stronie
- Ustaw tryb przesyłania głębokości na głębokość 16-bitowa
- Dodaj profil interakcji hand hand firmy Microsoft do profilów interakcji.
- Włącz następujące funkcje OpenXR:
- Śledzenie rąk
- Funkcje rzeczywistości mieszanej
- Model kontrolera ruchu
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ć.
Wybierz pozycję Odtwarzacz z menu listy po lewej stronie
- Wybierz kartę Ustawienia platformy uniwersalnej systemu Windows reprezentowaną jako ikona systemu Windows.
- Rozwiń inne ustawienia
- W obszarze Renderowanie zmień przestrzeń kolorów na Liniowa i uruchom ponownie aparat Unity, gdy zostanie wyświetlony monit.
- W obszarze Konfiguracja zmień aktywną obsługę danych wejściowych na Zarówno , jak i uruchom ponownie aparat Unity, gdy zostanie wyświetlony monit.
- Rozwijanie ustawień publikowania
- 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.
- W obszarze Obsługiwane rodziny urządzeń włącz system Holographic and Desktop
Zamknij lub zadokuj panel Ustawienia projektu
Otwieranie ustawień kompilacji plików>
- Wybieranie platformy uniwersalnej systemu Windows
- Skonfiguruj ustawienia, aby były zgodne z poniższymi ustawieniami
- Naciśnij przycisk Przełącz platformę .
Po zmianie platform przez aparat Unity zamknij panel kompilacji.
Weryfikowanie konfiguracji projektu
Wykonaj następujące kroki, aby sprawdzić, czy ustawienia projektu są poprawne.
Wybierz wpis ValidateProject z menu RemoteRendering na pasku narzędzi edytora aparatu Unity.
Przejrzyj okno ValidateProject pod kątem błędów i w razie potrzeby napraw ustawienia projektu.
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.
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.
Utwórz nowy skrypt języka C# o nazwie RemoteRenderingCoordinator. Projekt powinien wyglądać następująco:
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.
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.
- Utwórz nowy obiekt GameObject w scenie (Ctrl+Shift+N lub GameObject-Create> Empty) i nadaj mu nazwę RemoteRenderingCoordinator.
- Dodaj skrypt RemoteRenderingCoordinator do obiektu GameObject RemoteRenderingCoordinator .
- 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 . - Dodaj poświadczenia usługi Azure Remote Rendering, domenę konta i domenę usługi Remote Rendering do skryptu koordynatora:
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.
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.
- 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 .
Wybierz obiekt GameObject RemoteRenderingCoordinator i znajdź zdarzenie Aparatu Unity OnRequestingAuthorization uwidocznione w inspektorze składnika RemoteRenderingCoordinator .
Dodaj nowe zdarzenie, naciskając klawisz "+" w prawym dolnym rogu.
Przeciągnij składnik na własne zdarzenie, aby się odwołać.
Z listy rozwijanej wybierz pozycję RemoteRenderingCoordinator —> BypassAuthorization.
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).
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.
- 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ą.
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.
- Zastąp metody ConnectRuntimeToRemoteSession( ) i DisconnectRuntimeFromRemoteSession( ) ukończonymi wersjami poniżej.
- 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.
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.
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:
- Utwórz jednostkę zdalną.
- Utwórz lokalny obiekt GameObject reprezentujący jednostkę zdalną.
- Skonfiguruj lokalny obiekt GameObject, aby zsynchronizować jego stan (tj. Przekształć) z jednostką zdalną w każdej ramce.
- Ładowanie danych modelu z usługi Blob Storage do jednostki zdalnej.
- 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.
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.
Zapisz kod.
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.
W widoku Game (Gra) nie będzie widocznych zbyt wiele, jednak konsola pokaże stan zmiany aplikacji. Prawdopodobnie przejdzie
ConnectingToNewRemoteSession
do , i pozostanie tam, prawdopodobnie przez maksymalnie pięć minut.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.
Monitoruj dane wyjściowe konsoli — oczekiwanie na zmianę stanu na RuntimeConnected.
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.Obejrzyj konsolę danych wyjściowych metody ProgressHandler , która została przekazana do metody LoadModel .
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
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.