Tutorial: Interfaces e modelos personalizados

Neste tutorial, você aprenderá como:

  • Adicionar o kit de ferramentas de Realidade Misturada do Azure ao projeto
  • Gerenciar o estado do modelo
  • Configurar o Armazenamento de Blobs do Azure para ingestão de modelo
  • Carregar e processar modelos para renderização

Pré-requisitos

Introdução ao MRTK (Kit de Ferramentas de Realidade Misturada)

O MRTK (Kit de Ferramentas de Realidade Misturada) é um kit de ferramentas de multiplataforma para criação de experiências de realidade misturada. Usamos o MRTK 2.8.3 devido aos recursos de interação e visualização.

O guia oficial para importar o MRTK contém algumas etapas que não precisamos realizar. Apenas essas três etapas são necessárias:

  • Importando o "Toolkit de Realidade Misturada/Toolkit Foundation de Realidade Misturada" versão 2.8.3 para seu projeto por meio da Ferramenta de Recursos de Realidade Misturada (Importar o MRTK).
  • Execute o assistente de configuração do MRTK (Configurar o MRTK).
  • Adicione o MRTK à cena atual (Adicionar à cena). Use o ARRMixedRealityToolkitConfigurationProfile aqui em vez do perfil sugerido no tutorial.

Importar os ativos usados por este tutorial

Começando por este capítulo, implementaremos um padrão Model-View-Controller básico em grande parte do material abordado. A parte de model (ou modelo) do padrão é o código específico do Azure Remote Rendering, bem como o gerenciamento de estado relacionado ao Azure Remote Rendering. As partes de view (exibição) e controller (controlador) do padrão são implementadas usando ativos do MRTK e alguns scripts personalizados. É possível usar o modelo neste tutorial sem o view-controller (controlador de exibição) implementado aqui. Essa separação permite que você integre facilmente o código encontrado neste tutorial ao seu aplicativo, no qual ele assume a parte do view-controller do padrão de design.

Com a introdução do MRTK, há vários scripts, prefabs e ativos que podem ser adicionados ao projeto para dar suporte a interações e comentários visuais. Esses ativos, conhecidos como Ativos de Tutorial, são agrupados em um Pacote de Ativos do Unity, que está incluído no GitHub do Azure Remote Rendering em '\Unity\TutorialAssets\TutorialAssets.unitypackage'.

  1. Clone ou baixe o repositório git do Azure Remote Rendering. Se for baixar, extraia o zip para uma localização conhecida.
  2. No projeto do Unity, escolha Ativos -> Importar Pacote -> Pacote Personalizado.
  3. No explorador de arquivos, navegue até o diretório no qual você clonou ou descompactou o repositório do Azure Remote Rendering e selecione o .unitypackage encontrado em Unity -> TutorialAssets-> TutorialAssets.unitypackage
  4. Selecione o botão Importar para importar o conteúdo do pacote para seu projeto.
  5. No Editor do Unity, selecione Kit de Ferramentas de Realidade Misturada ->Utilitários -> Atualizar Sombreador Padrão do MRTK para o Pipeline de Renderização Leve na barra de menus superior e siga os prompts para atualizar o sombreador.

Depois que o MRTK e os Ativos do Tutorial estiverem configurados, verifique novamente se o perfil correto está selecionado.

  1. Selecione o GameObject MixedRealityToolkit na hierarquia de cenas.
  2. No Inspetor, no componente MixedRealityToolkit, mude o perfil de configuração para ARRMixedRealityToolkitConfigurationProfile.
  3. Pressione Ctrl+S para salvar as alterações.

Essa etapa configura o MRTK, principalmente, com os perfis padrão do HoloLens 2. Os perfis fornecidos são pré-configurados das seguintes maneiras:

  • Desligue o criador de perfil (pressione 9 para ativar/desativar ou diga "Mostrar/ocultar criador de perfil" no dispositivo).
  • Desligue o cursor de foco ocular.
  • Habilite os cliques de mouse do Unity, para que você possa clicar nos elementos da interface do usuário do MRTK usando o mouse em vez da mão simulada.

Adicionar o Menu do Aplicativo

A maioria dos controladores de exibição neste tutorial opera em classes base abstratas em vez de classes concretas. Esse padrão proporciona mais flexibilidade e nos permite fornecer os controladores de exibição a você, enquanto ainda ajuda você a aprender o código do Azure Remote Rendering. Para simplificar, não é fornecida uma classe abstrata à classe RemoteRenderingCoordinator, e seu controlador de exibição opera diretamente com a classe concreta.

Agora, você pode adicionar o prefab AppMenu à cena, para obter comentários visuais sobre o estado de sessão atual. O AppMenu também apresenta o painel modal que o usuário usa para autorizar o aplicativo a se conectar ao ARR.

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

  2. Arraste o prefab AppMenu para a cena.

  3. Se você vir uma caixa de diálogo do Importer do TMP, siga os prompts para Importar o TMP Essentials. Em seguida, feche a caixa de diálogo do importador, pois os exemplos e extras não são necessários.

  4. O AppMenu está configurado para conectar-se automaticamente e fornecer a caixa de diálogo restrita para consentir com a conexão a uma Sessão, de modo que podemos remover o bypass estabelecido anteriormente. No GameObject RemoteRenderingCoordinator, remova o bypass da autorização que implementamos anteriormente pressionando o botão '-' no evento Ao solicitar autorização.

    Remover bypass.

  5. Teste o controlador de exibição pressionando Executar no Editor do Unity.

  6. No Editor, agora que o MRTK está configurado, você pode usar as teclas WASD para alterar a posição da exibição e manter o botão direito do mouse pressionado enquanto move o mouse para alterar a direção da exibição. Experimente "passear" um pouco pela cena para ter uma ideia dos controles.

  7. No dispositivo, você pode levantar a palma para convocar o AppMenu; no Editor do Unity, use a tecla de atalho 'M'.

  8. Se perder o menu de vista, pressione a tecla 'M' para convocá-lo. O menu é colocado próximo à câmera para facilitar a interação.

  9. O AppMenu apresenta um elemento de interface do usuário para autorização à direita do AppMenu. De agora em diante, você deve usar esse elemento de interface do usuário para autorizar o aplicativo a gerenciar sessões de renderização remota.

    Autorização da interface do usuário

  10. Pare a reprodução do Unity para continuar com o tutorial.

Gerenciar o estado do modelo

Precisamos de um novo script chamado RemoteRenderedModel para acompanhar o estado, resposta a eventos, acionamento de eventos e configuração. Essencialmente, RemoteRenderedModel armazena o caminho remoto para os dados do modelo em modelPath. Ele escuta as alterações de estado no RemoteRenderingCoordinator para ver se deve carregar ou descarregar automaticamente o modelo definido por ele. O GameObject que tem o RemoteRenderedModel anexado a ele é o pai local do conteúdo remoto.

Observe que o script RemoteRenderedModel implementa BaseRemoteRenderedModel, incluído nos Ativos do Tutorial. Essa conexão permite que o controlador de exibição de modelo remoto seja associado ao seu script.

  1. Crie um script chamado 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);
        }
    }
    

Na definição mais básica, RemoteRenderedModel contém os dados necessários para carregar um modelo (nesse caso, o SAS ou o URI builtin:// ) e acompanha o estado do modelo remoto. Quando chega a hora de carregar o modelo, o método LoadModel é chamado em RemoteRenderingCoordinator e a entidade que contém o modelo é retornada para referência e descarregamento.

Carregar o modelo de teste

Vamos testar o novo script carregando o modelo de teste novamente. Para esse teste, precisamos de um Game Object para conter o script e ser um pai para o modelo de teste, e também precisamos de um estágio virtual que contenha o modelo. O estágio permanece fixo em relação ao mundo real usando um WorldAnchor. Usamos um estágio fixo para que o modelo em si ainda possa ser movido posteriormente.

  1. Crie um GameObject vazio na cena e dê a ele o nome ModelStage.

  2. Adicionar um componente de Âncora mundial ao ModelStage

    Adicionar um componente WorldAnchor

  3. Crie um novo objeto de jogo vazio como um filho de ModelStage e dê a ele o nome de TestModel.

  4. Adicione o script RemoteRenderedModel a TestModel.

    Adicionar o componente RemoteRenderedModel

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

    Especificar detalhes do modelo

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

    Posicionar o objeto

  7. Verifique se AutomaticallyLoad está ativado.

  8. Pressione Executar no Editor do Unity para testar o aplicativo.

  9. Conceda autorização clicando no botão Conectar para permitir que o aplicativo crie uma sessão, conecte-se e ela e carregue o modelo automaticamente.

Observe o Console conforme o aplicativo percorre seus estados. Tenha em mente que alguns estados podem levar algum tempo para serem concluídos e talvez não tenha atualizações de progresso por um tempo. Por fim, você vê os logs do carregamento do modelo e, logo em seguida, o modelo de teste renderizado na cena.

Tente mover e girar o GameObject TestModel usando a Transformação no Inspetor ou na exibição Cena e observe as transformações na exibição Game.

Log do Unity

Provisionar o Armazenamento de Blobs no Azure e ingestão de modelo personalizado

Agora, podemos tentar carregar seu modelo. Para fazer isso, você precisa configurar o Armazenamento de Blobs no Azure, carregar e converter um modelo e, em seguida, carregar o modelo usando o script RemoteRenderedModel. As etapas de carregamento do modelo personalizado podem ser ignoradas com segurança se você não tem seu modelo para carregar no momento.

Siga as etapas especificadas no Início Rápido: Converter um modelo para renderização. Ignore a seção Inserir novo modelo no Aplicativo de Exemplo de Início Rápido desse tutorial. Quando tiver o URI da SAS (Assinatura de Acesso Compartilhado) de seu modelo ingerido, continue.

Carregar e renderizar um modelo personalizado

  1. Crie um GameObject vazio na cena e dê a ele um nome semelhante ao do modelo personalizado.

  2. Adicione o script RemoteRenderedModel ao GameObject recém-criado.

    Adicionar o componente RemoteRenderedModel

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

  4. Preencha o Model Path com o URI da SAS (Assinatura de Acesso Compartilhado) do modelo criado na etapa Provisionar Armazenamento de Blobs no Azure e ingestão de modelo personalizado.

  5. Posicione o objeto GameObject na frente da câmera, na posição x = 0, y = 0, z = 3.

  6. Verifique se AutomaticallyLoad está ativado.

  7. Pressione Executar no Editor do Unity para testar o aplicativo.

    O console mostra o estado de sessão atual e também o modelo carregando mensagens de progresso, assim que a sessão for conectada.

  8. Remova o objeto de modelo personalizado da cena. A melhor experiência desse tutorial é com o modelo de teste. Embora haja suporte para vários modelos no ARR, este tutorial foi escrito para dar melhor suporte a um só modelo remoto por vez.

Próximas etapas

Agora, você pode carregar seus modelos no Azure Remote Rendering e exibi-los em seu aplicativo! Em seguida, orientamos você na manipulação dos seus modelos.