Руководство по Интерфейсы и пользовательские модели

В этом руководстве описано следующее:

  • Добавление набора средств Смешанной реальности в проект
  • Управление состоянием модели
  • Настройка Хранилища BLOB-объектов Azure для приема модели
  • Отправка и обработка моделей с целью преобразования для просмотра

Предварительные требования

Начало работы с набором средств для Смешанной реальности

Набор средств для Смешанной реальности (MRTK) — это кроссплатформенный набор средств для создания функций смешанной реальности. Мы используем MRTK 2.8.3 для функций взаимодействия и визуализации.

Официальное руководство по импорту MRTK содержит некоторые действия, которые нам не нужно делать. Необходимо выполнить только следующие три шага.

  • Импорт Смешанная реальность Toolkit/Смешанная реальность Toolkit Foundation версии 2.8.3 в проект с помощью средства Смешанная реальность Feature Tool (Импорт MRTK).
  • Запустите мастер настройки MRTK (настройка MRTK).
  • Добавьте MRTK в текущую сцену (Добавить в сцену). Используйте здесь ARRMixedRealityToolkitConfigurationProfile вместо рекомендуемого профиля в этом руководстве.

Импорт ресурсов, используемых в этом учебнике

Начиная с этой главы мы реализуем базовый шаблон модели— представления — контроллера для большей части рассматриваемых материалов. Часть модель этого шаблона представляет собой специальный код для службы "Удаленная отрисовка Azure" и отвечает за связанное с ней управление состоянием. Части представление и контроллер шаблона реализуются с помощью ресурсов MRTK и ряда пользовательских скриптов. Модель в этом руководстве можно использовать без контроллера представления, реализованного здесь. Это разделение позволяет легко интегрировать код, приведенный в этом руководстве, в собственное приложение, где оно принимает часть представления-контроллера шаблона проектирования.

С появлением MRTK появилось несколько скриптов, заготовок и ресурсов, которые теперь можно добавить в проект для поддержки взаимодействия и визуальной обратной связи. Эти ресурсы, называемые ресурсами учебника, объединяются в пакет ресурсов Unity, который входит в azure Удаленная отрисовка GitHub в папке \Unity\TutorialAssets\TutorialAssets.unitypackage.

  1. Клонируйте или скачайте репозиторий Git для Удаленной отрисовки Azure. В случае скачивания извлеките ZIP-файл в известное расположение.
  2. В проекте Unity выберите Активы —> Импорт пакета —> Пользовательский пакет.
  3. В проводнике перейдите в каталог, в котором вы клонировали или распаковали репозиторий azure Удаленная отрисовка, а затем выберите найденный .unitypackage в Unity —> TutorialAssets —> TutorialAssets.unitypackage.
  4. Нажмите кнопку Import (Импорт), чтобы импортировать содержимое пакета в свой проект.
  5. В редакторе Unity выберите Смешанная реальность Toolkit —> Utilities —> Upgrade MRTK Standard Shader for Lightweight Render Pipeline (Стандартный шейдер MRTK для упрощенного конвейера отрисовки) в верхней строке меню и следуйте инструкциям по обновлению шейдера.

После настройки MRTK и ресурсов учебника дважды проверка выбран правильный профиль.

  1. В иерархии сцены выберите игровой объект MixedRealityToolkit.
  2. В инспекторе в компоненте MixedRealityToolkit смените профиль конфигурации на ARRMixedRealityToolkitConfigurationProfile.
  3. Нажмите клавиши CTRL+S, чтобы сохранить изменения.

На этом шаге в MRTK настраивается в основном профили HoloLens 2 по умолчанию. Предоставленные профили предварительно настроены следующими способами.

  • Профилировщик отключен (нажмите клавишу 9, чтобы его включить или отключить, или скажите Show/Hide Profiler (Показать/скрыть профилировщик) на устройстве).
  • Курсор взгляда отключен.
  • Щелчки мыши Unity включены, поэтому для выбора элементов пользовательского интерфейса MRTK вместо имитации руки можно использовать мышь.

Добавление меню приложения

Большинство контроллеров представления, описанных в этом учебнике, работают с базовыми абстрактными, а не конкретными классами. Этот шаблон обеспечивает большую гибкость и позволяет нам предоставлять контроллеры представления, помогая при этом изучать код Удаленной отрисовки Azure. Для простоты класс RemoteRenderingCoordinator не имеет абстрактного класса, а его контроллер представления работает непосредственно с конкретным классом.

Теперь можно добавить в сцену префаб AppMenu, чтобы обеспечить визуальную обратную связь для текущего состояния сеанса. AppMenu также представляет модальную панель, которую пользователь использует для авторизации приложения для подключения к ARR.

  1. Найдите префаб AppMenu в папке Assets/RemoteRenderingTutorial/Prefabs/AppMenu.

  2. Перетащите префаб AppMenu на сцену.

  3. Если появится диалоговое окно импорта TMP, следуйте инструкциям по импорту TMP Essentials. Затем закройте диалоговое окно импорта, так как примеры и дополнительные сведения не требуются.

  4. Префаб AppMenu настроен для автоматической привязки и предоставления модальных средств получения согласия на подключение к сеансу, поэтому мы можем удалить средство обхода, размещенное ранее. Удалите из игрового объекта RemoteRenderingCoordinator средство обхода авторизации, реализованное ранее. Для этого нажмите кнопку "-" для события On Requesting Authorization (При запросе авторизации).

    Удалите обход.

  5. Проверьте контроллер представления, нажав Play (Воспроизвести) в редакторе Unity.

  6. Теперь, когда набор MRTK настроен, вы можете менять расположение представления с помощью клавиш W, A, S и D, а также изменять его направление, перемещая указатель мыши и удерживая нажатой ее правую кнопку. Поэкспериментируйте со сценой, чтобы освоиться с управлением.

  7. Чтобы вызвать AppMenu, можно поднять ладонь вверх на устройстве или использовать клавишу доступа "M" в редакторе Unity.

  8. Если вы потеряли меню из виду, нажмите клавишу "M", чтобы его вызвать. Меню размещается рядом с камерой для удобства взаимодействия.

  9. AppMenu представляет элемент пользовательского интерфейса для авторизации справа от AppMenu. Теперь этот элемент пользовательского интерфейса следует использовать для авторизации приложения для управления сеансами удаленной отрисовки.

    Авторизация пользовательского интерфейса

  10. Остановите воспроизведение в Unity, чтобы продолжить работу с учебником.

Управление состоянием модели

Нам нужен новый скрипт с именем RemoteRenderedModel , который предназначен для отслеживания состояния, реагирования на события, запуска событий и настройки. По сути, RemoteRenderedModel сохраняет удаленный путь для данных модели в modelPath. Он прослушивает изменения состояния в RemoteRenderingCoordinator , чтобы узнать, следует ли автоматически загружать или выгружать определяемую модель. GameObject, к которому подключен remoteRenderedModel , является локальным родительским элементом удаленного содержимого.

Обратите внимание, что скрипт RemoteRenderedModel реализует элемент BaseRemoteRenderedModel, добавленный из учебных ресурсов. Это подключение позволяет контроллеру удаленного представления модели привязаться к скрипту.

  1. Создайте скрипт с именем RemoteRenderedModel в той же папке, что и RemoteRenderingCoordinator. Замените все его содержимое кодом, приведенным ниже.

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

Вообще говоря, RemoteRenderedModel содержит данные, необходимые для загрузки модели (в данном случае SAS или builtin:// URI), и позволяет отслеживать состояние удаленной модели. Когда нужно загрузить модель, LoadModel метод вызывается в RemoteRenderingCoordinator, а сущность, содержащая модель, возвращается для ссылки и выгрузки.

Загрузка тестовой модели

Давайте протестируем новый скрипт, загрузив тестовую модель снова. Для этого теста нам нужен игровой объект, содержащий скрипт и родительский для тестовой модели, а также виртуальный этап, содержащий модель. Этап остается фиксированным относительно реального мира с помощью WorldAnchor. Мы используем фиксированный этап, чтобы саму модель по-прежнему можно было перемещать в дальнейшем.

  1. Создайте пустой игровой объект в сцене и назовите его ModelStage.

  2. Добавление компонента WorldAnchor в ModelStage

    Добавление компонента WorldAnchor

  3. Создайте пустой игровой объект в качестве дочернего для ModelStage и назовите его TestModel.

  4. Добавьте скрипт RemoteRenderedModel в TestModel.

    Добавление компонента RemoteRenderedModel

  5. Заполните поля Model Display Name и Model Path значениями TestModel и builtin://Engine соответственно.

    Настройка параметров модели

  6. Поместите объект TestModel перед камерой в положении x = 0, y = 0, z = 3.

    Размещение объекта

  7. Убедитесь, что функция AutomaticallyLoad включена.

  8. Нажмите Play (Воспроизвести) в редакторе Unity, чтобы протестировать приложение.

  9. Предоставьте авторизацию, нажав кнопку Подключить , чтобы позволить приложению создать сеанс, подключиться к нему и автоматически загрузить модель.

Наблюдайте за консолью по мере смены состояний продвижения. Имейте в виду, что для завершения некоторых состояний может потребоваться некоторое время, и в течение некоторого времени обновления хода выполнения могут не выполняться. В конечном итоге вы увидите журналы из загрузки модели, а затем вскоре после отрисоченной тестовой модели в сцене.

Попробуйте переместить и повернуть TestModel GameObject с помощью преобразования в инспекторе или в представлении сцены и наблюдать за преобразованиями в представлении игры.

Журнал Unity

Подготовка Хранилища BLOB-объектов в Azure и прием настраиваемых моделей

Теперь можно попробовать загрузить собственную модель. Для этого необходимо настроить хранилище BLOB-объектов в Azure, отправить и преобразовать модель, а затем загрузить модель с помощью скрипта RemoteRenderedModel . Если в настоящее время у вас нет собственной модели для загрузки, можно просто пропустить действия по загрузке настраиваемой модели.

Выполните действия, указанные в документе Краткое руководство. Преобразование модели для отрисовки. Пропустите раздел Вставка новой модели в пример приложения быстрого запуска для этого руководства. Получив URI подписанного URL-адреса (SAS) для вашей модели, продолжайте.

Загрузка настраиваемой модели и ее преобразование для просмотра

  1. Создайте пустой игровой объект в сцене и назовите его так же, как настраиваемую модель.

  2. Добавьте скрипт RemoteRenderedModel в созданный игровой объект.

    Добавление компонента RemoteRenderedModel

  3. Заполните поле Model Display Name соответствующим именем модели.

  4. Model Path Укажите URI подписанного URL-адреса модели (SAS), созданного на шаге Подготовка хранилища BLOB-объектов в Azure и прием пользовательских моделей.

  5. Поместите игровой объект перед камерой в положении x = 0, y = 0, z = 3.

  6. Убедитесь, что функция AutomaticallyLoad включена.

  7. Нажмите Play (Воспроизвести) в редакторе Unity, чтобы протестировать приложение.

    В консоли отображается текущее состояние сеанса, а также сообщения о ходе загрузки модели после подключения сеанса.

  8. Удалите объект настраиваемой модели из сцены. Для работы с этим руководством лучше всего использовать тестовую модель. Хотя в ARR поддерживается несколько моделей, это руководство было написано для оптимальной поддержки одной удаленной модели за раз.

Дальнейшие действия

Теперь вы можете загружать собственные модели в службу "Удаленная отрисовка Azure" и просматривать их в своем приложении. Далее мы поможем вам управлять моделями.