Tutorial: Interfaces e modelos personalizados

Neste tutorial, ficará a saber como:

  • Adicionar Mixed Reality Toolkit ao projeto
  • Gerir o estado do modelo
  • Configurar Armazenamento de Blobs do Azure para ingestão de modelos
  • Carregar e processar modelos para composição

Pré-requisitos

Introdução ao Mixed Reality Toolkit (MRTK)

O Mixed Reality Toolkit (MRTK) é um toolkit de várias plataformas para criar experiências de realidade mista. Utilizamos o MRTK 2.8.3 para as funcionalidades de interação e visualização.

O guia oficial para importar o MRTK contém alguns passos que não precisamos de seguir. Apenas estes três passos são necessários:

  • Importar o "Mixed Reality Toolkit/Mixed Reality Toolkit Foundation" versão 2.8.3 para o seu projeto através da Ferramenta de Funcionalidades do Mixed Reality (Importar MRTK).
  • Execute o assistente de configuração do MRTK (Configurar MRTK).
  • Adicione MRTK à cena atual (Adicionar à cena). Utilize o ARRMixedRealityToolkitConfigurationProfile aqui em vez do perfil sugerido no tutorial.

Importar recursos utilizados por este tutorial

A partir deste capítulo, vamos implementar um padrão básico de model-view-controller para grande parte do material abrangido. A parte do modelo do padrão é o código específico do Azure Remote Rendering e a gestão de estado relacionada com o Azure Remote Rendering. As partes de vista e controlador do padrão são implementadas com recursos mrTK e alguns scripts personalizados. É possível utilizar o modelo neste tutorial sem o controlador de vista implementado aqui. Esta separação permite-lhe integrar facilmente o código encontrado neste tutorial na sua própria aplicação, onde assume a parte do controlador de vista do padrão de estrutura.

Com a introdução do MRTK, existem vários scripts, prefabs e recursos que podem agora ser adicionados ao projeto para suportar interações e feedback visual. Estes recursos referidos como Recursos do Tutorial são agrupados num Pacote de Recursos do Unity, que está incluído no Azure Remote Rendering GitHub em '\Unity\TutorialAssets\TutorialAssets.unitypackage'.

  1. Clone ou transfira o repositório git do Azure Remote Rendering, se transferir o zip para uma localização conhecida.
  2. No seu projeto do Unity, selecione Recursos -> Pacote de Importação -> Pacote Personalizado.
  3. No explorador de ficheiros, navegue para o diretório onde clonou ou deszipou o repositório Remote Rendering do Azure e, em seguida, selecione o .unitypackage localizado em Unity -> TutorialAssets -> TutorialAssets.unitypackage
  4. Selecione o botão Importar para importar o conteúdo do pacote para o seu projeto.
  5. No Editor do Unity, selecione Mixed Reality Toolkit –> Utilitários –> Atualizar o Standard Shader do MRTK para o Pipeline de Composição Leve a partir da barra de menus superior e siga as instruções para atualizar o sombreado.

Assim que o MRTK e os Recursos do Tutorial estiverem configurados, verifique se o perfil correto está selecionado.

  1. Selecione MixedRealityToolkit GameObject na hierarquia de cenários.
  2. No Inspetor, no componente MixedRealityToolkit , mude o perfil de configuração para ARRMixedRealityToolkitConfigurationProfile.
  3. Prima Ctrl+S para guardar as alterações.

Este passo configura o MRTK, principalmente, com os perfis de HoloLens 2 predefinidos. Os perfis fornecidos estão pré-configurados das seguintes formas:

  • Desative o gerador de perfis (prima 9 para o ativar/desativar ou diga "Mostrar/Ocultar Perfil" no dispositivo).
  • Desative o cursor de olhar para os olhos.
  • Ative os cliques do rato do Unity, para que possa clicar em elementos da IU do MRTK com o rato em vez da mão simulada.

Adicionar o Menu da Aplicação

A maioria dos controladores de vista neste tutorial funcionam em classes de base abstratas em vez de em classes concretas. Este padrão proporciona mais flexibilidade e permite-nos fornecer-lhe os controladores de vista, ajudando-o ainda a aprender o código Remote Rendering do Azure. Para simplificar, a classe RemoteRenderingCoordinator não tem uma classe abstrata fornecida e o controlador de vista funciona diretamente na classe de betão.

Agora, pode adicionar o AppMenu pré-fabricado à cena, para obter feedback visual sobre o estado da sessão atual. O AppMenu também apresenta o painel modal que o utilizador utiliza para autorizar a aplicação a ligar ao ARR.

  1. Localize o appMenu prefab em Assets/RemoteRenderingTutorial/Prefabs/AppMenu

  2. Arraste a prefab appMenu para a cena.

  3. Se vir uma caixa de diálogo para o Importador de TMP, siga as instruções para Importar O TMP Essentials. Em seguida, feche a caixa de diálogo do importador, uma vez que não são necessários exemplos e extras.

  4. O AppMenu está configurado para ligar automaticamente e fornecer o modal para consentir a ligação a uma Sessão, para que possamos remover o bypass colocado anteriormente. No RemoteRenderingCoordinator GameObject, remova o bypass para autorização que implementámos anteriormente, premindo o botão "-" no evento Ao Pedir Autorização .

    Remover bypass.

  5. Teste o controlador de vista ao premir Reproduzir no Editor do Unity.

  6. No Editor, agora que o MRTK está configurado, pode utilizar as teclas WASD para alterar a posição da vista e premir o botão direito do rato + mover o rato para alterar a direção da vista. Experimente "conduzir" à volta do local um pouco para sentir os controlos.

  7. No dispositivo, pode levantar a palma da mão para convocar o AppMenu, no Editor do Unity, utilize a tecla de atalho "M".

  8. Se perdeu de vista o menu, prima a tecla "M" para convocar o menu. O menu é colocado perto da câmara para uma interação fácil.

  9. O AppMenu apresenta um elemento de IU para autorização à direita do AppMenu. A partir de agora, deve utilizar este elemento de IU para autorizar a aplicação a gerir sessões de composição remota.

    Autorização da IU

  10. Pare o Unity de jogar para continuar com o tutorial.

Gerir o estado do modelo

Precisamos de um novo script chamado RemoteRenderedModel para controlar o estado, responder a eventos, eventos de acionamento e configuração. Essencialmente, RemoteRenderedModel armazena o caminho remoto para os dados do modelo no modelPath. Escuta as alterações de estado no RemoteRenderingCoordinator para ver se deve carregar ou descarregar automaticamente o modelo que define. O GameObject que tem o RemoteRenderedModel anexado ao mesmo é o principal local para o conteúdo remoto.

Repare que o script RemoteRenderedModel implementa BaseRemoteRenderedModel, incluído nos Recursos do Tutorial. Esta ligação permite ao controlador de vista de modelo remoto vincular-se ao script.

  1. Crie um novo script com o nome RemoteRenderedModel na mesma pasta que RemoteRenderingCoordinator. Substitua todo o conteúdo pelo seguinte código:

    // 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 UnityEngine;
    using UnityEngine.Events;
    
    public class RemoteRenderedModel : BaseRemoteRenderedModel
    {
        public bool AutomaticallyLoad = true;
    
        private ModelState currentModelState = ModelState.NotReady;
    
        [SerializeField]
        [Tooltip("The friendly name for this model")]
        private string modelDisplayName;
        public override string ModelDisplayName { get => modelDisplayName; set => modelDisplayName = value; }
    
        [SerializeField]
        [Tooltip("The URI for this model")]
        private string modelPath;
        public override string ModelPath
        {
            get => modelPath.Trim();
            set => modelPath = value;
        }
    
        public override ModelState CurrentModelState
        {
            get => currentModelState;
            protected set
            {
                if (currentModelState != value)
                {
                    currentModelState = value;
                    ModelStateChange?.Invoke(value);
                }
            }
        }
    
        public override event Action<ModelState> ModelStateChange;
        public override event Action<float> LoadProgress;
        public override Entity ModelEntity { get; protected set; }
    
        public UnityEvent OnModelNotReady = new UnityEvent();
        public UnityEvent OnModelReady = new UnityEvent();
        public UnityEvent OnStartLoading = new UnityEvent();
        public UnityEvent OnModelLoaded = new UnityEvent();
        public UnityEvent OnModelUnloading = new UnityEvent();
    
        public UnityFloatEvent OnLoadProgress = new UnityFloatEvent();
    
        public void Awake()
        {
            // Hook up the event to the Unity event
            LoadProgress += (progress) => OnLoadProgress?.Invoke(progress);
    
            ModelStateChange += HandleUnityStateEvents;
        }
    
        private void HandleUnityStateEvents(ModelState modelState)
        {
            switch (modelState)
            {
                case ModelState.NotReady:  OnModelNotReady?.Invoke();  break;
                case ModelState.Ready:     OnModelReady?.Invoke();     break;
                case ModelState.Loading:   OnStartLoading?.Invoke();   break;
                case ModelState.Loaded:    OnModelLoaded?.Invoke();    break;
                case ModelState.Unloading: OnModelUnloading?.Invoke(); break;
            }
        }
    
        private void Start()
        {
            //Attach to and initialize current state (in case we're attaching late)
            RemoteRenderingCoordinator.CoordinatorStateChange += Instance_CoordinatorStateChange;
            Instance_CoordinatorStateChange(RemoteRenderingCoordinator.instance.CurrentCoordinatorState);
        }
    
        /// <summary>
        /// Listen for state changes on the coordinator, clean up this model's remote objects if we're no longer connected.
        /// Automatically load if required
        /// </summary>
        private void Instance_CoordinatorStateChange(RemoteRenderingCoordinator.RemoteRenderingState state)
        {
            switch (state)
            {
                case RemoteRenderingCoordinator.RemoteRenderingState.RuntimeConnected:
                    CurrentModelState = ModelState.Ready;
                    if (AutomaticallyLoad)
                        LoadModel();
                    break;
                default:
                    UnloadModel();
                    break;
            }
        }
    
        private void OnDestroy()
        {
            RemoteRenderingCoordinator.CoordinatorStateChange -= Instance_CoordinatorStateChange;
            UnloadModel();
        }
    
        /// <summary>
        /// Asks the coordinator to create a model entity and listens for coordinator state changes
        /// </summary>
        [ContextMenu("Load Model")]
        public override async void LoadModel()
        {
            if (CurrentModelState != ModelState.Ready)
                return; //We're already loaded, currently loading, or not ready to load
    
            CurrentModelState = ModelState.Loading;
    
            ModelEntity = await RemoteRenderingCoordinator.instance?.LoadModel(ModelPath, this.transform, SetLoadingProgress);
    
            if (ModelEntity != null)
                CurrentModelState = ModelState.Loaded;
            else
                CurrentModelState = ModelState.Error;
        }
    
        /// <summary>
        /// Clean up the local model instances
        /// </summary>
        [ContextMenu("Unload Model")]
        public override void UnloadModel()
        {
            CurrentModelState = ModelState.Unloading;
    
            if (ModelEntity != null)
            {
                var modelGameObject = ModelEntity.GetOrCreateGameObject(UnityCreationMode.DoNotCreateUnityComponents);
                Destroy(modelGameObject);
                ModelEntity.Destroy();
                ModelEntity = null;
            }
    
            if (RemoteRenderingCoordinator.instance.CurrentCoordinatorState == RemoteRenderingCoordinator.RemoteRenderingState.RuntimeConnected)
                CurrentModelState = ModelState.Ready;
            else
                CurrentModelState = ModelState.NotReady;
        }
    
        /// <summary>
        /// Update the Unity progress event
        /// </summary>
        /// <param name="progressValue"></param>
        public override void SetLoadingProgress(float progressValue)
        {
            LoadProgress?.Invoke(progressValue);
        }
    }
    

Nos termos mais básicos, RemoteRenderedModel contém os dados necessários para carregar um modelo (neste caso, o SAS ou builtin:// URI) e controla o estado do modelo remoto. Quando é altura de carregar o modelo, o LoadModel método é chamado em RemoteRenderingCoordinator e a Entidade que contém o modelo é devolvida para referência e descarregamento.

Carregar o Modelo de Teste

Vamos testar o novo script ao carregar novamente o modelo de teste. Para este teste, precisamos de um Objeto de Jogo para conter o script e ser um elemento principal do modelo de teste e também precisamos de uma fase virtual que contenha o modelo. O palco permanece fixo em relação ao mundo real usando um WorldAnchor. Utilizamos uma fase fixa para que o modelo em si possa continuar a ser movido mais tarde.

  1. Crie um novo Objeto de Jogo vazio na cena e dê-lhe o nome ModelStage.

  2. Adicionar um componente World Anchor ao ModelStage

    Adicionar componente WorldAnchor

  3. Crie um novo Objeto de Jogo vazio como um subordinado do ModelStage e dê-lhe o nome TestModel.

  4. Adicione o script RemoteRenderedModel a TestModel.

    Adicionar componente RemoteRenderedModel

  5. Preencha o Model Display Name e com Model Path "TestModel" e "builtin://Engine", respetivamente.

    Especificar detalhes do modelo

  6. Posicione o objeto TestModel à frente da câmara, na posição x = 0, y = 0, z = 3.

    Objeto de posição

  7. Certifique-se de que a opção Carregar Automaticamente está ativada.

  8. Prima Reproduzir no Editor do Unity para testar a aplicação.

  9. Conceda autorização ao clicar no botão Ligar para permitir que a aplicação crie uma sessão, ligue-se à mesma e carregue automaticamente o modelo.

Veja a Consola à medida que a aplicação progride através dos respetivos estados. Tenha em atenção que alguns estados podem demorar algum tempo a concluir e poderá não haver atualizações de progresso durante algum tempo. Eventualmente, verá os registos do modelo a carregar e, em seguida, pouco depois do modelo de teste composto na cena.

Experimente mover e rodar o TestModel GameObject através da Transformação no Inspetor ou na vista Cena e observe as transformações na vista Jogo.

Registo do Unity

Aprovisionar o Armazenamento de Blobs no Azure e a ingestão de modelos personalizados

Agora, podemos tentar carregar o seu próprio modelo. Para tal, tem de configurar o Armazenamento de Blobs no Azure, carregar e converter um modelo e, em seguida, carregar o modelo com o script RemoteRenderedModel . Os passos de carregamento do modelo personalizado podem ser ignorados em segurança se não tiver o seu próprio modelo para carregar neste momento.

Siga os passos especificados no Início Rápido: Converter um modelo para composição. Ignore a secção Inserir novo modelo na Aplicação de Exemplo de Início Rápido para este tutorial. Assim que tiver o URI de Assinatura de Acesso Partilhado (SAS) do modelo ingerido, continue.

Carregar e compor um modelo personalizado

  1. Crie um novo GameObject vazio na cena e dê-lhe um nome semelhante ao seu modelo personalizado.

  2. Adicione o script RemoteRenderedModel ao GameObject recentemente criado.

    Adicionar componente RemoteRenderedModel

  3. Preencha o Model Display Name com um nome adequado para o modelo.

  4. Preencha o com o Model Path URI de Assinatura de Acesso Partilhado (SAS) do modelo que criou no passo Aprovisionar Armazenamento de Blobs no Azure e no passo de ingestão de modelos personalizados .

  5. Posicione o GameObject em frente à câmara, na posição x = 0, y = 0, z = 3.

  6. Certifique-se de que a opção Carregar Automaticamente está ativada.

  7. Prima Reproduzir no Editor do Unity para testar a aplicação.

    A consola mostra o estado da sessão atual e também o modelo a carregar mensagens de progresso, assim que a sessão estiver ligada.

  8. Remova o objeto de modelo personalizado da cena. A melhor experiência para este tutorial é com o modelo de teste. Apesar de vários modelos serem suportados no ARR, este tutorial foi escrito para suportar melhor um único modelo remoto de cada vez.

Passos seguintes

Agora pode carregar os seus próprios modelos para o Azure Remote Rendering e vê-los na sua aplicação! Em seguida, orientamo-lo ao longo da manipulação dos seus modelos.