Kurz: Zobrazení vzdáleně vykreslovaných modelů

V tomto kurzu se naučíte:

  • Zřízení instance Azure Remote Rendering (ARR)
  • Vytvoření a zastavení relace vykreslování
  • Opakované použití existující relace vykreslování
  • Připojení a odpojení od relací
  • Načtení modelů do relace vykreslování

Požadavky

Pro tento kurz potřebujete:

  • Aktivní předplatné Azure s platbou za předplatné Azure Vytvoření účtu
  • Windows SDK 10.0.18362.0 (stáhnout)
  • Nejnovější verze Visual Studio 2019 (stáhnout)
  • GIT (stáhnout)
  • Unity (viz požadavky na systém pro podporované verze)
  • Střední znalost Unity a jazyka C# (například vytváření skriptů a objektů, použití předfabů, konfigurace událostí Unity atd.)

Zřízení instance Azure Remote Rendering (ARR)

Pokud chcete získat přístup Azure Remote Rendering službě, musíte nejprve vytvořit účet.

Vytvoření nového projektu Unity

Tip

Úložiště ukázek ARR obsahuje projekt se všemi dokončené kurzy, který je možné použít jako referenci. Úplný projekt Unity najdete ve složce Unity\Tutorial-Complete.

V Unity Hubu vytvořte nový projekt. V tomto příkladu předpokládáme, že se projekt vytváří ve složce s názvem RemoteRendering.

Nový projekt Unity

Zahrnutí Azure Remote Rendering balíčku

Postupujte podle pokynů k přidání balíčku Azure Remote Rendering do projektu Unity.

Konfigurace fotoaparátu

  1. Vyberte uzel Hlavní kamera.

  2. Kliknutím pravým tlačítkem na komponentu Transformovat otevřete místní nabídku a vyberte možnost Resetovat:

    resetování transformace fotoaparátu

  3. Nastavte možnost Vymazat příznaky na plnou barvu.

  4. Nastavte Pozadí na Černé (#000000) s plně transparentním (0) alfa (A)

    Barevné kolečko

  5. Nastavte Roviny oříznutí na Near = 0,3 a Far = 20. To znamená, že vykreslení oříznutí geometrie, která je blíže než 30 cm nebo větší než 20 metrů.

    Vlastnosti kamery Unity

Úprava nastavení projektu

  1. Otevřete Upravit > nastavení projektu...

  2. V nabídce seznamu vlevo vyberte Kvalita.

  3. Změňte výchozí úroveň kvality všech platforem na Nízké. Toto nastavení umožní efektivnější vykreslování místního obsahu a neovlivní kvalitu vzdáleně vykreslených obsahu.

    změna nastavení kvality projektu

  4. V nabídce seznamu vlevo vyberte Grafika.

  5. Změňte nastavení Skriptovatelný kanál vykreslování na HybridRenderingPipeline.
    Snímek obrazovky, který ukazuje, kde můžete změnit nastavení Skriptovatelný kanál vykreslování na HybridRenderingPipeline
    V některých případech uživatelské rozhraní nevyplní seznam dostupných typů kanálů z balíčků. Pokud k tomu dojde, musí se asset HybridRenderingPipeline přetáhnout do pole ručně:
    změna nastavení grafiky projektu

    Poznámka

    Pokud nemůžete přetáhnout asset HybridRenderingPipeline do pole Asset kanálu vykreslování (pravděpodobně kvůli tomu, že pole neexistuje), ujistěte se, že konfigurace balíčku obsahuje com.unity.render-pipelines.universal balíček.

  6. V nabídce seznamu vlevo vyberte Player (Přehrávač).

  7. Vyberte kartu Univerzální platforma Windows nastavení, která je reprezentována ikonou Windows.

  8. Změňte nastavení XR tak, aby podporovalo Windows Mixed Reality, jak je znázorněno níže:

    1. Povolení podporované virtuální reality
    2. Stiskněte tlačítko +a přidejte Windows Mixed Reality
    3. Nastavte formát hloubky na 16bitovou hloubku.
    4. Ujistěte se, že je povolené sdílení hloubkové vyrovnávací paměti.
    5. Nastavte Stereo Rendering Mode (Režim stereo vykreslování) na Single Pass Instanced (Instanced s jedním průchodem).

    player settings (nastavení přehrávače)

  9. Ve stejném okně nad XR Settings (Nastavení XR) rozbalte Publishing Settings (Nastavení publikování).

  10. Přejděte dolů na Capabilities (Možnosti) a vyberte:

    • InternetClient
    • InternetClientServer
    • SpatialPerception
    • PrivateNetworkClientServer (volitelné). Tuto možnost vyberte, pokud chcete ke svému zařízení připojit vzdálený ladicí program Unity.
  11. V části Podporované rodiny zařízení povolte Holographic a Desktop.

  12. Zavření nebo ukotvení panelu Nastavení projektu

  13. Otevření nastavení sestavení >souboru

  14. Vyberte Univerzální platforma Windows

  15. Nakonfigurujte nastavení tak, aby odpovídala nastavením, která najdete níže.

  16. Stiskněte tlačítko *Přepnout platformu.* nastavení sestavení

  17. Jakmile Unity změní platformy, zavřete panel sestavení.

Ověření nastavení projektu

Provedením následujících kroků ověřte správnost nastavení projektu.

  1. V nabídce RemoteRendering na panelu nástrojů editoru Unity zvolte položku ValidateProject.

  2. V okně ValidateProject zkontrolujte chyby a v případě potřeby opravte nastavení projektu.

    Ověření projektu editoru Unity

Poznámka

Pokud ve svém projektu použijete MRTK a povolíte subsystém fotoaparátu, MRTK přepíše ruční změny, které použijete u fotoaparátu. To zahrnuje opravy z nástroje ValidateProject.

Vytvoření skriptu pro koordinaci Azure Remote Rendering připojení a stavu

Existují čtyři základní fáze zobrazení vzdáleně vykreslených modelů, které jsou popsané v níže popsaném vývojovém diagramu. Každá fáze musí být provedena v pořadí. Dalším krokem je vytvoření skriptu, který bude spravovat stav aplikace a procházet jednotlivé požadované fáze.

Zásobník ARR 0

  1. V podokně Projekt v části Prostředky vytvořte novou složku s názvem RemoteRenderingCore. Potom uvnitř RemoteRenderingCore vytvořte další složku s názvem Scripts ( Skripty).

  2. Vytvořte nový skript C# s názvem RemoteRenderingCoordinator. Váš projekt by měl vypadat takhle:

    Hierarchie projektu

    Tento koordinovací skript bude sledovat a spravovat stav vzdáleného vykreslování. Všimněte si, že část tohoto kódu se používá k udržování stavu, vystavení funkcí jiným komponentách, aktivaci událostí a ukládání dat specifických pro aplikaci, která přímo souvisejí s Azure Remote Rendering. Jako výchozí bod použijte následující kód a později v tomto kurzu budeme řešit a implementovat konkrétní Azure Remote Rendering kódu.

  3. V editoru kódu otevřete RemoteRenderingCoordinator a nahraďte celý jeho obsah následujícím kódem:

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

Vytvoření objektu Azure Remote Rendering GameObject

Koordinátor vzdáleného vykreslování a jeho požadovaný skript (ARRServiceUnity) jsou objekty MonoBehaviour, které musí být připojeny k objektu GameObject ve scéně. ArrServiceUnity poskytuje skript ARR, který zpřístupňuje velkou část funkcí arr pro připojení ke vzdáleným relacím a jejich správu.

  1. Vytvořte ve scéně nový objekt GameObject (Ctrl+Shift+N nebo GameObject->Create Empty) a pojmnte ho RemoteRenderingCoordinator.
  2. Přidejte skript RemoteRenderingCoordinator do objektu RemoteRenderingCoordinator GameObject.
    Přidání komponenty RemoteRenderingCoordinator
  3. Ověřte, že se do objektu GameObject automaticky přidá skript ARRServiceUnity, který se v inspektoru zobrazí jako Služba. Pokud vás zajímá, je to výsledek v horní části skriptu [RequireComponent(typeof(ARRServiceUnity))] RemoteRenderingCoordinator.
  4. Přidejte své Azure Remote Rendering přihlašovací údaje, doménu účtu a doménu Remote Rendering domény do skriptu koordinátora:
    Přidání přihlašovacích údajů

Inicializace Azure Remote Rendering

Teď, když máme rozhraní pro koordinátora, implementujeme každou ze čtyř fází počínaje polem Initialize Remote Rendering.

Stack ARR 1

Inicializace Azure Remote Rendering, který objekt fotoaparátu se má použít k vykreslení, a postupuje stavovým strojem do notauthorized. To znamená, že je inicializovaný, ale zatím nemá oprávnění pro připojení k relaci. Vzhledem k tomu, že spuštění relace SMĚROVÁNÍ žádostí na aplikace má náklady, musíme potvrdit, že uživatel chce pokračovat.

Při zadávání stavu NotAuthorized se volá CheckAuthorization, která vyvolá událost RequestingAuthorization a určí, které přihlašovací údaje účtu se mají použít (AccountInfo se definuje v horní části třídy a používá přihlašovací údaje, které jste definovali prostřednictvím inspektoru Unity v kroku výše).

Poznámka

Rekompilace modulu runtime není podporována směrováním žádostí na aplikace. Úprava skriptu a jeho uložení v aktivním režimu přehrávání může vést k zamrznutí Unity a k vynucení vypnutí prostřednictvím správce úloh. Před úpravou skriptů se vždy ujistěte, že jste zastavili režim přehrávání.

  1. Nahraďte obsah polí InitializeARR a InitializeSessionService dokončený kódem níže:
/// <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 bylo možné postupovat od NotAuthorized do NoSession, obvykle bychom uživateli prezentují modální dialogové okno, aby si mohli vybrat (a přesně to uděláme v jiné kapitole). Teď automaticky obcházíme kontrolu autorizace voláním byPassAuthentication ihned po aktivaci události RequestingAuthorization.

  1. Vyberte RemoteRenderingCoordinator GameObject a vyhledejte událost OnRequestingAuthorization Unity zveřejněnou v inspektoru komponenty RemoteRenderingCoordinator.

  2. Stisknutím klávesy +v pravém dolním rohu přidejte novou událost.

  3. Přetáhněte komponentu na vlastní událost, abyste na sebe odkazovat.
    Obejít ověřování\

  4. V rozevíracím seznamu vyberte RemoteRenderingCoordinator -> BypassAuthorization.
    Snímek obrazovky znázorňující vybranou možnost RemoteRenderingCoordinator.BypassAuthorization

Vytvoření nebo připojení vzdálené relace

Druhou fází je vytvoření nebo připojení Remote Rendering relace (další informace najdete v Remote Rendering relace).

Stack ARR 2

Ve vzdálené relaci se modely vykreslí. Metoda JoinRemoteSession( ) se pokusí připojit k existující relaci, sledovaná vlastností LastUsedSessionID nebo pokud je na sessionIDOverride přiřazené ID aktivní relace. SessionIDOverride je určený pouze pro účely ladění, měl by se používat pouze tehdy, když víte, že relace existuje a chcete se k ní explicitně připojit.

Pokud nejsou k dispozici žádné relace, vytvoří se nová relace. Vytvoření nové relace je ale časově náročná operace. Proto byste se měli pokusit vytvořit relace jenom v případě potřeby a kdykoli je to možné znovu použít (další informace o správě relací najdete v tématu Připraveno pro komerční použití: sdružování relací, plánování a osvědčené postupy).

Tip

Funkce StopRemoteSession() ukončí aktivní relaci. Pokud chcete zabránit zbytečným poplatkům, měli byste relace vždy zastavit, když už nejsou potřeba.

Stavový počítač teď bude v závislosti na dostupných relacích postupovat do stavu ConnectingToNewRemoteSession nebo ConnectingToExistingRemoteSession. Otevření existující relace nebo vytvoření nové relace aktivuje událost ARRSessionService.OnSessionStatusChanged a spustí naši metodu OnRemoteSessionStatusChanged. V ideálním případě by to mělo za následek rozšíření stavového počítače na RemoteSessionReady.

  1. Pokud se chcete připojit k nové relaci, upravte kód tak, aby nahradil metody JoinRemoteSession( ) a StopRemoteSession( ) dokončenou ukázkou níže:
/// <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();
    }
}

Pokud chcete ušetřit čas opakovaném používáním relací, nezapomeňte deaktivovat možnost Automaticky zastavit relaci v komponentě ARRServiceUnity. Mějte na paměti, že relace tak budou spuštěné, i když k nim není nikdo připojený. Vaše relace může běžet po dobu, po kterou maxleaseTime před vypnutím serverem (hodnotu MaxLeaseTime je možné upravit v koordinátoru Remote Rendering v části Nové výchozí nastavení relace). Na druhé straně pokud při odpojování automaticky vypnete všechny relace, budete muset pokaždé počkat na spuštění nové relace, což může být poněkud dlouhý proces.

Poznámka

Zastavení relace se projeví okamžitě a nelze ho vrátit zpět. Po zastavení musíte vytvořit novou relaci se stejnou režií při spuštění.

Připojení místního modulu runtime ke vzdálené relaci

Dále musí aplikace připojit svůj místní modul runtime ke vzdálené relaci.

Stack ARR 3

Aplikace také potřebuje naslouchat událostem týkajícím se připojení mezi runtime a aktuální relací. Tyto změny stavu se zpracovávají v onLocalRuntimeStatusChanged. Tento kód přejde do stavu ConnectingToRuntime. Po připojení v OnLocalRuntimeStatusChanged přejde stav na RuntimeConnected. Připojení k modulu runtime je posledním stavem, kterým se koordinátor zabývá. To znamená, že aplikace se provádí se všemi běžnými konfiguracemi a je připravená zahájit práci na načítání a vykreslování modelů specifickou pro relaci.

  1. Nahraďte metody ConnectRuntimeToRemoteSession( ) a DisconnectRuntimeFromRemoteSession( ) dokončenou verzí níže.
  2. Je důležité si poznamenat metodu Unity LateUpdate a aktualizovat aktuální aktivní relaci. Aktuální relace tak může odesílat a přijímat zprávy a aktualizovat vyrovnávací paměť snímků o rámce přijaté ze vzdálené relace. Správné fungování směrování žádostí na aplikace je velmi důležité.
/// <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;
}

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

Poznámka

Připojení místního modulu runtime ke vzdálené relaci závisí na volání aktualizace v aktuálně aktivní relaci. Pokud zjistíte, že vaše aplikace nikdy neposouvá stav ConnectingToRuntime, ujistěte se, že v aktivní relaci pravidelně voláte update.

Načtení modelu

Po nastavení požadovaného základu jste připraveni načíst model do vzdálené relace a začít přijímat snímky.

Diagram znázorňuje tok procesu pro přípravu na načtení a zobrazení modelu

Metoda LoadModel je navržená tak, aby přijímila cestu modelu, obslužnou rutinu průběhu a nadřazenou transformaci. Tyto argumenty se budou používat k načtení modelu do vzdálené relace, aktualizaci uživatele při průběhu načítání a orientaci vzdáleně vykreslený model na základě nadřazené transformace.

  1. Metodu LoadModel zcela nahraďte následujícím kódem:

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

Výše uvedený kód provádí následující kroky:

  1. Vytvořte vzdálenou entitu.
  2. Vytvořte místní objekt GameObject, který bude reprezentovat vzdálenou entitu.
  3. Nakonfigurujte místní objekt GameObject tak, aby synchronizoval svůj stav (tj. transformovat) se vzdálenou entitou při každém snímku.
  4. Načtěte data modelu Blob Storage do vzdálené entity.
  5. Pro pozdější použití vraťte nadřazenou entitu.

Zobrazení testovacího modelu

Teď máme veškerý kód potřebný k zobrazení vzdáleně vykreslených modelů a všechny čtyři požadované fáze pro vzdálené vykreslování jsou dokončené. Teď potřebujeme přidat malý kód, který spustí proces načítání modelu.

Stack ARR 4

  1. Do třídy RemoteRenderingCoordinator přidejte následující kód hned pod metodu 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)}%"));
    }
    

    Tento kód vytvoří objekt GameObject, který bude fungovat jako nadřazený objekt testovacího modelu. Potom zavolá metodu LoadModel pro načtení modelu "builtin://Engine", což je prostředek integrovaný do Azure Remote Rendering pro účely testování vykreslování.

  2. Uložte kód.

  3. Stisknutím tlačítka Přehrát v Editoru Unity spusťte proces připojení k Azure Remote Rendering a vytvoření nové relace.

  4. V zobrazení Game toho moc neuvidíte, ale konzola zobrazí stav změny aplikace. Pravděpodobně postupuje do ConnectingToNewRemoteSession a zůstane tam, pravděpodobně až pět minut.

  5. Vyberte RemoteRenderingCoordinator GameObject a zobrazte jeho připojené skripty v inspektoru. Sledujte, jak aktualizace součásti služby probíhá v průběhu inicializace a kroků připojení.

  6. Monitorování výstupu konzoly – čekání na změnu stavu na RuntimeConnected.

  7. Po připojení modulu runtime klikněte v inspektoru pravým tlačítkem na RemoteRenderingCoordinator a zpřístupňuje místní nabídku. Potom v místní nabídce klikněte na možnost Model zátěžového testu, kterou jste přidali v části [ContextMenu("Load Test Model")] našeho kódu výše.

    Načtení z místní nabídky

  8. Sledujte výstup objektu ProgressHandler, který jsme předáli do metody LoadModel, v konzole.

  9. Podívejte se na vzdáleně vykreslený model.

Poznámka

Vzdálený model nebude nikdy viditelný v zobrazení scény, pouze v zobrazení hry. Je to proto, že ARR vykresluje snímky vzdáleně speciálně pro perspektivu kamery herního zobrazení a neví o fotoaparátu editoru (který se používá k vykreslení zobrazení scény).

Další kroky

Načtený model

Gratulujeme! Vytvořili jste základní aplikaci, která dokáže zobrazit vzdáleně vykreslené modely pomocí Azure Remote Rendering. V dalším kurzu integrujeme MRTK a naimportujeme vlastní modely.