В этой статье речь пойдет об оптимизации Unity-сцены проекта Plantsim 1.0.: о визуальной части цифровой копии предприятия Tennessee Eastman Process, реализованного на Unity 2017.1.1f1.


image


Заметка от партнера IT-центра МАИ и организатора магистерской программы “VR/AR & AI” — компании PHYGITALISM.


В первой версии работа осуществлялась с использованием мощного компьютера, а так же стороннего вычислительного устройства — PLC. Отличие новой версии заключается в том, что теперь это WEB-приложение, реализованное с помощью WebGL, и для его работы потребуется только ноутбук.


Платформа Unity Команда Зачем
PlantSim 1.0 PC на базе: Intel i7 Nvidia GTX 1070ti 2017.3.0f3 Default Pipeline Несколько 3D художников Энтузиазм моделить и творить красоту
PlantSim 2.0 Ноутбук: Intel i5 Nvidia 1050ti Google Chrome 2019.2.15f1 LWPR Один 3D художник Ещё больше энтузиазма победить страшную задачу

Таблица сравнения разработки проектов


В сравнении с приложением PlantSim 1.0, заметно снижение требований к характеристикам компьютера. Поэтому оптимизация стала главной задачей для PlantSim 2.0.


image
Пайплайн разработки PlantSim 2.0


Анализ предстоящей работы


WebGL — это работа 3D графики с использованием возможностей браузеров Google Chrome, Mozilla, Safari. Первостепенными задачами для нас являлись оптимизация, сохранение того же уровня реализма и поддержание работоспособности приложения на 30FPS+. Для этого нам предстояло работать в пайплайне мобильной разработки.


image
Unity: WebGL Build


Пример проекта с изображении выше можно посмотреть здесь и самому ощутить качество графики и скорость отрисовки.


Красивой визуализации в WebGL добиться можно, но сложно. Имеется ряд особенностей, о которых желательно знать, если вы собираетесь сделать подобный проект самостоятельно и/или работаете с 3D графикой впервые.


Перед тем, как начать непосредственно работу над 3D, мы выстроили четкий план со списком моделей, которые необходимо оптимизировать.


image
Таблица с планом оптимизации для отслеживания хода работы


Вот краткий список моделей, что присутствовали в сцене раньше:


  • OutPutTank — 2 шт
  • Reactor — 1 шт, модель была раздельная
  • ReactorExplosion — 1 шт, отдельная анимации разлета уничтоженного реактора
  • Condenser — 1 шт
  • Separator — 1 шт
  • PipesSystem — различная система труб на сцене
  • Stripper — 1 шт
  • Valve — 6 шт
  • Tank — 4 шт
  • Refrigerator — 1 шт
  • Compressor — 3 шт
    В PlantSim 1.0 на сцене было 189 736 трисов, и главной задачей для нас была оптимизация модели на 60%. Таким образом мы получим 75 895 треугольников, что будет удовлетворять нашему полигональному бюджету.
    image
    График расчета суммарного предполагаемого количества полигонов после оптимизации

Оптимизация меша


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


image
Модель Reactor (PlantSim 1.0) — анализ иерархии и сетки


  • Нелогичные наименования объектов
  • Много отдельных элементов
  • Сложная топология

Сам Reactor имел 21998 треугольников. По плану нам было необходимо избавиться от всего лишнего и малозначительного, и получить 8800 треугольников. Перед тем, как начать оптимизировать, мы проанализировали объекты вновь и заметили новую важную деталь: модели OutPutTank, Reactor и Striper имели одни и те же элементы — основание и лестницу. Это означало, что эти элементы можно дублировать и использовать для них один материал.


В итоге, после работы над Reactor объектом (это первый объект, с которого началась оптимизация), мы получили новую модель в 7258 треугольников. Напомню, что по плану было 8000. При условии, что теперь UV карта одна, объект представляет собой объединенный меш и один материал — стало ясно, что задачу мы сможем реализовать, а итоговая сумма треугольников окажется намного меньше, чем планировали изначально.


image
Модель Reactor (PlantSim 2.0) —оптимизированная иерархия и сетка


  • Исправили наименования объектов
  • Уменьшили количество отдельных элементов
  • Упростили и оптимизировали топологию

Объект ReactorExplosion имел сложную анимацию взрыва, красивую и эффектную — разлет маленьких кусков реактора с сохранением только его основания. После первой полной сборки сцены наш FPS сильно проседал непосредственно на моменте взрыва, который помимо этой анимации меша так же имел и анимацию, состоящую из 1000 частиц инструмента Particle System. Появился вопрос, что создавало трудности для отрисовки: анимация или частицы? Мы обсудили задачу с Unity разработчиком и выявили, что оба фактора слишком тяжелые для WebGL движка, поэтому было решено оптимизировать эффект взрыва, используя 5 частиц (это возможно благодаря FlipBook текстурам). Со стороны разработки так же была необходима оптимизация анимации внутри Unity.


FlipBook — это покадровая анимация, вложенная в одну текстуру. Соответственно, чем больше кадров, тем больше разрешение самой текстуры и ее вес. Этот способ отрисовки анимации наиболее оптимальный, так как вместо огромного количества частиц и сложных симуляций мы обходимся одной или несколькими частицами, в которых за один цикл проигрывается наш взрыв. Таким образом, используя всего 5 частиц, был оптимизирован эффект с исходными 1000.


С анимацией было сложнее, так как каждый осколок Reactor был индивидуальным и имел свою запеченную анимацию — это и тянуло WebGL вниз по FPS. Со стороны Unity разработки было предложено запечь всю анимацию в один объект, а метод отрисовки с Mesh renderer переключить на Skinned mesh renderer. Таким образом вместо одновременного перемещения тысячи transform points у нас была цепочка костей и один transform points (Origin самого объекта). Как итог, FPS вырос с 3–5 во время воспроизведения эффекта до 15–20 во время запуска анимации, благо этот фриз сохранялся лишь 0,5 секунды. Кстати, ReactorExplosion объект имел 41220 треугольников, и в угоду экономии времени, которого было мало, было решено не создавать новую анимацию с нуля, а использовать старую, оптимизируя описанным способом.


В итоге работа по оптимизации меша закончилась успешно. Выше мы упоминали, что на сцене располагалось 189736 треугольников (если не учитывать ReactorExplosion, то 148516 треугольников). Отнимая 60% всей геометрии, мы хотим получить на выходе 59403 треугольника (после оптимизации). У нас получилось 61064 треугольника, что превысило наши планы по оптимизации, но все равно было намного ниже полигонального бюджета, выставленного в самом начале проекта. С учетом ReactorExplosion было 102284 треугольника, что так же было около границы обозначенного бюджета.


Задача выполнена, переходим к текстурированию.


image
График расчета суммарного фактического количества полигонов после оптимизации


Текстурирование


Текстурирование играет важную роль в визуализации моделей. Если вы никогда не слышали о том, из чего состоит модель в ее привычном виде, то давайте вспомним:


  • Base Color/Albedo — RGBA изображение, определяющее, какой цвет у поверхностей. Иногда идет с Alpha каналом прозрачности поверхностей.
  • Ambient Occlusion — Ч/Б изображение с информацией затенений объекта.
  • Metallness — Ч/Б изображение, определяющее металлическую природу поверхности.
  • Smoothness — Ч/Б изображение, определяющее степень размытости поверхности или ее мелких отдельных деталей.
  • Normal Map — RGB изображение, симулирующее светотень от мелких неровностей поверхности.
  • Height Map — Ч/Б изображение, отвечающее за степень искажения неровностей поверхности. Часто используется в связке с Normal Map.
  • Emission — RGB изображение, отвечающее за самосвечение отдельных участков или всей поверхности объекта. Может влиять на Global Illumination параметры, то есть участвовать в освещении других объектов в сцене.
    image
    Набор текстур для модели Striper из проекта PlantSim 2.0

Благодаря текстурным картам имитируются поверхности на моделях. После анализа списка можно заметить сходства — помимо RGB текстур мы имеем много черно-белых карт. Что же это значит для оптимизации?


Дело в том, что отдельно R G и B, а также A каналы и представляют собой черно-белый слой. А значит отдельно в одну текстуру RGBа можно зашифровать сразу четыре текстуры. Как это работает:


image
Комбинирование четырех ч/б текстурных карт в одну текстуру


Далее эту Combine texture расшифровывают и отдают в отдельные каналы материала в Unity.
Также хотелось бы упомянуть Normal Map. Эта текстура — мощный инструмент для оптимизации модели, с помощью нее возможно избавиться от мелкой детализации модели и впоследствии отобразить при рендеринге на сцене.


image
Сравнение двух моделей Compressor


На этом примере наглядно видна разница поверхностей старой модели и новой, оптимизированной. Данную текстуру создают двумя способами:


  1. Отрисовка вручную в специальных программах, например Substance Painter, используя подготовленные кисти неровностей.
  2. Запекание неровностей с HighPoly модели (с повышенной детализацией, фасками, неровностями и углублениями) на LowPoly модель, которая максимально оптимизирована. Данную процедуру используют как в Substance Painter, так и в Blender инструментах.

Материалы и шейдеры


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


image
Набор материалов на различных объектах. Один цвет — один материал


На примере с помощью цвета отображено, какие материалы используются на том или ином объекте. Можно заметить, что один и тот же материал используются на разных объектах, то есть они имеют одну UV развертку. Это важный момент, так как если объект маленький и незначительный, значит сама UV развертка будет маленькой и менее детальной для объекта.


Объединение подобных объектов с другими позволяет экономить на количестве суммарно используемых текстурных карт.


WebGL ограничен в отрисовке графики, а также в реализации функционала. Но если учитывать эти особенности в разработке, то можно найти варианты замены или обхода ограничений. Главное — проведение исследования каждой возникающей проблемы.


Запекание света


Давайте поговорим о том, как работает свет в Unity, а именно о типе освещения Global Illumination. Это “честный” способ отображения отражения света от поверхности объекта и создания теней. В Unity тени строятся по двум типам объектов — Dynamic и Static объекты.


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


В случае статичных объектов подразумевается, что они находятся в покое, а тени всегда сохраняют свое положение. Как раз для таких объектов можно запечь тень объекта на пространство в специальный Lightmap текстуры. Учитывая задачу оптимизации и отказ от Realtime просчета теней, это наиболее оптимальный подход.
image
Представление сцены без Lightmap текстурной карты и после ее создания
В PlantSim 2.0 мы запекали тени от всех объектов, если они не участвовали в анимациях.


Хитрости в работе с WebGL


К финалу разработки мы стали сражаться за количество FPS уже в собранных билдах, отслеживая напрямую в Google Chrome. Сохранялась проблема — почему FPS на ноутбуке c i7 процессором и GTX 1070ti не повышается больше 30, а наоборот, иногда даже проседает. Ведь не может быть так, что всей проделанной работы было недостаточно.


Перед тем, как запускать проект на референсном ноутбуке заказчика, мы проводили тесты на различных устройствах (ноутбуки, персональные компьютеры), и анализировали FPS, которое показывало приложение.


image
Таблица стресс-тестов на различных устройствах. Скриншоты фиксируют одинаковую ситуацию и количество FPS в данный момент.


В тестировании участвовали как мощные ноутбуки на базе Windows и Apple MacBook PRO, так и слабые ноутбуки, на которых не предполагалась работа с 3D графикой. Для чистоты тестирования мы так же использовали стационарный компьютер с RTX 2080ti (кстати, на нем FPS был стабильно 60). FPS оказался разным у всех устройств.


Разгадка оказалась куда проще, чем предполагалось изначально. Дело в том, что работу браузера Google Chrome определяет некоторые факторы:


  1. Подключен ли ноутбук к зарядке? Если нет, то вероятно, что мощность видеокарты значительно снижена из за экономии аккумулятора.
  2. Есть ли на ноутбуке интегрированная видеокарта (видеокарта, которая встроена в центральный процессор или материнскую плату)?

Проверить первый пункт достаточно легко, второй — уже сложнее. Мы попробовали дать конкретную задачу для приложения Google Chrome, сделать запуск с параметрами повышенной мощности, однако результатов это не давало. Видеокарты фирмы NVIDIA имеют отдельное приложение по управлению ресурсами видеокарты на устройстве — NVIDIA control panel. В этом приложении мы нашли Google Chrome и вручную выставили работу с помощью дискретной видеокарты, а не интегрированной.


image
Окно Nvidia Control Panel — настройки производительности для отдельного приложения


В итоге, после очередного тестирования билда на Google Chrome, счетчик FPS победно показывал 70–90 — это означало, что во всех предыдущих тестированиях в отрисовке 3D графики браузера участвовала куда более слабая видеокарта (интегрированная), из-за стараний ноутбука сэкономить заряд батареи.


Вывод


Разработка проекта такого рода специфична, но во многом интересна, так как WebGL — это новое направление работы с 3D графикой и представление ее в WEB.


Все описанные методы выше не новые, но важные и влияют на скорость загрузки приложения, и насколько будет комфортно пользователю просматривать в браузере.


Полезные ссылки: