Использование Unity3D в нативном iOS/Android приложении для моделирования освещения открытых пространств

    image

    Unity3D известнейшая платформа для разработки 3D и 2D игр, завоевавшая популярность во всем мире. В то же время ее возможности не ограничены разработкой только игровых приложений, а подходят для применения в любых других областях, требующих создания кроссплатформенных приложений для работы с графикой. В этой статье мы расскажем об опыте использования Unity3D для разработки системы расчета освещения открытых пространств.

    Заказчик нашего проекта — международная светотехническая корпорация «БООС ЛАЙТИНГ ГРУПП». В целях расширения привлекательности своей продукции и упрощения взаимодействия с клиентами ему потребовалось разработать приложение, позволяющее визуально смоделировать расположение осветительных приборов, а также провести расчет освещенности поверхности и вывести необходимую техническую информацию в отчете. Предполагалось, что приложение запускается на iPad или Android планшете потенциальным клиентом или торговым представителем и позволяет клиенту сразу получить представление о возможности осветительных установок.

    В общем виде, приложение представляет собой редактор, позволяющий добавлять и редактировать элементы освещения, дорог, декоративных элементов, произвести светотехнический расчет сцены, вывести отчет в pdf. Каждый элемент имеет свой набор параметров для редактирования и подтипы, влияющие на его отображение и расчет.

    • Имеются несколько видов мачт освещения, с различными типам крепления светильников, углами наклона светильников и длиной выноса. Для определенных типов светильников возможна индивидуальная настройка с указанием направления освещения.

      image
       
    • Дороги могут быть линейными участками, элементами дуги, площадью, кольцом. Для каждого элемента может быть настроены размеры, положение, тип разметки, слой.

      image
    • Декоративные элементы — машины, деревья, кустарники, дорожные знаки

    Все элементы сцены можно вращать и перемещать. Также поддерживаются стандартные действия вернуть назад или повторить операцию. Общие настройки проекта позволяют задать текстуру дорг, поверхности земли, отображение дополнительных параметров. Сцена отображается в 2D/3D режимах. А при расчете освещения на поверхности отображается карта освещенности поверхности в фиктивных цветах.

    image


    Весь UI по возможности требовалось делать нативными средствами iOS/Android.
    Основное техническое требование к приложению — это уметь рассчитать освещение сцены согласно технической спецификации светильников. Также требовалась возможность для каждого светильника отображать и просматривать его диаграмму направленности (кривые сил света) в 3D/2D режимах.

    Выбор платформы


    Для реализации проекта нами был выбран Unity как более удобный нам для реализации требуемого функционала. Вообще в нашей компании имелся опыт работы и с другими 3D движками и платформами (OpenSceneGraph, Ogre3D, LibGdx) и технически они все могут справится с требуемой задачей, но в этот раз выбор пал на Unity, который позволяет удобнее управлять сценой в процессе разработки и отлаживать в процессе работы.

    Основные сложности


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

    Работа с нативным UI


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

    image


    К счастью, Unity умеет экспортировать проект, который можно использовать как основу для нативного приложения. Основная сложность в данном случае заключается в том как интегрировать в полученный проект дополнительный UI. Также, не менее важно то, что при сборке проекта Unity его формат и расположение файлов формируются Unity и частично перезаписывается, что ограничивает возможность модификации проекта.

    При разработке iOS приложения мы воспользовались механизмом предложенным в статье. Во время разработки использовалась Unity 5.5 и на данный момент указанное в ней может потерять актуальность. При сборке андроид проекта дополнительных проблем не возникало за исключение того, что Unity каждый раз перезаписывает манифест файл и файлы ресурсов.
    Дополнительная проблема состоит в том, что Unity может работать только в одном окне. В тоже время, нам требовалось обеспечить работу Unity для отображения всей сцены, а также при открытии окна настроек должна была отображаться 3D модель фотометрического тела светильника. Для осуществления этого пришлось воспользоваться “хаком” и использовать один и тот же объект UIView в различных окнах.

    Для передачи сообщений мы воспользовались стандартным функционалом предлагаемым Unity. То есть, все сообщения были в формате json и передавались простыми строками. На производительность это не накладывало никаких ограничений поскольку размер сообщений максимум доходил до 100 символов, а их частота определялась скоростью работы с программой. В тоже время, в более требовательных приложениях имеет смысл сделать собственный обработчик сообщений как представлено на хабре здесь и здесь.

    Расчет освещения


    Все источники освещения, используемые в приложении поставляются в стандартном IES формате, который описывает распределение света в различном направлениию (спецификация). Этот формат широко используется в профессиональных CAD системах и 3D редакторах. Он представляет собой текстовой файл с указанием интенсивности света в различных направлениях и дополнительную метаинформацию с указанием типа, общей интенсивности источника, осей и плоскостей симметрии. Учитывая симметрию светильников, ies файл может быть очень маленьким. Например, в случае осевой симметрии, достаточно указать раследеление света только в одной плоскости.

    Пример простого IES файла
    IESNA91[TEST]
    Simple demo intensity distribution [MANUFAC]
    Lightscape Technologies, Inc.
    TILT=NONE
    1
    -1
    1
    8
    1
    1
    2
    0.0 0.0 0.0
    1.0 1.0 0.0
    0.0 5.0 10.0 20.0 30.0 45.0 65.0 90.0
    0.0
    1000.0 1100.0 1300.0 1150.0 930.0 650.0 350.0 0.0


    Для отображения диаграммы направленности света использовались два вида отображения:

    • Кривые сил света (КСС) — двумерный график на котором представлена интенсивность света в одной из главных плоскостей в зависимости от направления. Этот график для удобства может быть представлен как в полярной так и декартовой системе координат

      image
    • Фотометрическое тело — трехмерное изображение интенсивности света в разном направлении

      image

    Расчетный модуль


    Для расчета освещения у заказчика имелся собственный C++ модуль, используемый в других продуктах компании, а поэтому требовалось интегрировать его в Unity проект. Порядок подключения модуля отличался от используемой платформы.

    • На iOS платформе Unitу умеет непосредственно вызывать С функции, поэтому достаточно скопировать исходники модуля непосредственно в проект и добавить классы для его взаимодействия с Unity. Классы возможно хранить как непосредственно в проекте iOS, так и в папке плагинов, которые автоматически копируются при экспорта проекта в XCode. Пример вызова C++ функций следующий:

      [DllImport("__Internal")]
      public static extern void calculateLight([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Light[] lights, int size, ref CalculationResult result);
    • На Android платформе С++ модуль должен быть предварительно скомпилирован в отдельную библиотеку. Это можно сделать непосредственно добавлением исходников С++ в проект и настройкой gradle для их сборки в so библиотеки.
    • Также, для отладки и тестирования Unity части разработка велась на windows машине, поэтому потребовалось подключить исходники модуля и в Windows. Это делается аналогично андроид проекту, только в этом случае файлы скачала собираются в dll библиотеку и подключались к проекту.

    Отображение карты освещенности


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

    image


    Как ранее упоминалось весь расчет производился подключаемым модулем C++ в который передавались данные об источниках цвета. Результатом расчета был двумерный массив интенсивности света по всей поверхности сцены с заданной детализацией.

    Полученная карта освещенности анализировалась на минимальное, максимальное значение, по которым строилась одномерная текстура градиента (GradientRamp). Используя эту текстуру интенсивность освещения преобразовывалась в фиктивные цвета непосредственно во фрагментном шейдере. При этом для различных поверхностей дорог и поверхности земли использовался один и тот же шейдер, а переключение режима освещения обеспечивалось с использованием "multi-compile" шейдера.

    Генерация Pdf файла


    В соответствии с техническими требованиями для пользователя должен был генерироваться отчет, содержащий информацию об общей сцене (размеры, ее изображения, параметры освещения) и информацию о каждом использованном типе светильников с указанием их положения, направления характеристик, а также диаграммами КСС и отображением фотометрического тела.
    Поскольку отчет должен был отображаться как на iOS так и Android, то его генерацию необходимо было производить непосредственно в Unity модуле, а потом уже отображать стандартными нативными средствами.

    image

    Для построения pdf была выбрана библиотека iTextSharp, отвечающая нашим требованиям. Создание отчета в ней не представляет особой сложности и заключается в создание блоков текста, таблиц, изображений непосредственно из кода. Однако, в ходе разработки мы столкнулись с множеством нюансов, решение которых иногда требовало значительных усилий. Основной проблемой из которых был запуск генерации отчета в фоновом потоке.

    Если при тестировании на десктопной машине генерация pdf составляла порядка нескольких секунд, то при тестировании на iPad mini 3 это время легко достигло 1-3 минут. Естественно, создание отчета потребовалось перенести в отдельный поток, для избегания проблем с подвисанием интерфейса. В общем случае это не является проблемой, но это не так при использовании Unity, в котором явно запрещено использовать Unity API не из главного потока. В тоже время, для отчета нам требовалось, как минимум, рендерить КСС и изображение сцены, что необходимо делать только из главного потока.

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

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

    UniRx


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

    В нашем приложении мы предпочли пойти прямым путем и запускать задачи последовательно то в главном, то в фоновом потоках. Проблема была только в том, как именно организовать такой запуск задач, чтобы не погрязнуть в этой мешанине и корректно синхронизировать операции.
    Существенную помощь в решении этой проблемы принесло использование Rx его воплощение в виде бесплатного ассета UniRx, о котором на хабре уже подробно рассказывалось здесь и здесь.

    Его использование позволило значительно упростить взаимодействие между потоками и в примере ниже показано можно запустить несколько методов в строгой последовательности, но в разных потоках

    Пример кода
    var initializer = Observable.FromCoroutine(initMethod);
    var heavyMethod1 = Observable.Start(() => doHardWork());
    var mainThread1 = Observable.FromCoroutine(renderImage);
    var heavyMethod2 = Observable.Start(() => doHardWork2());
    
    initializer.SelectMany(heavyMethod1)
            .SelectMany(mainThread1)
            .SelectMany(heavyMethod2)
            .ObserveOnMainThread()
            .Subscribe((x) => done())
            .AddTo(this);
    

    В этом примере последовательно будет запущен метод doHardWork() в фоновом потоке. После его завершения запустится renderImage() в основном потоке, а после выполнится doHardWork2() опять в фоновом потоке.

    Также стоит отметить, что в ходе анализа генерации отчета на быстродействие было обнаружено, что наиболее медленная часть это внедрение изображений в отчет. Поиск в интернете показал, что мы не единственные кто сталкиваются с этой проблемой, но подходящего нам решения не нашлось. Нам пришлось несколько снизить качество изображений до приемлемого уровня, что дало прирост в скорости на 20-40%.

    Таким образом, в созданном нами приложении получилось удачно внедрить графический движок Unity в нативное приложение iOS/Android. Это отличается от традиционного подхода когда Unity является главной частью приложения и обращается к специфичным свойствам системы через систему плагинов. В тоже время наш подход может быть полезен при необходимости разработки сложного нативного интерфейса, в который требуется встроить нетривиальную 3D графику.

    SimbirSoft

    123,00

    Лидер в разработке современных ИТ-решений на заказ

    Поделиться публикацией
    Комментарии 10
      0
      Замечательное приложение, жаль только, что рассчитано на одного производителя. Есть Dialux, к которому можно скачать базы почти всего, что светит и греет.

      Силуэт Бэтмена и его аппарата на этом рисунке получились случайно, или это тоже заказ БЛ Групп?

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

        «Бэтмен» появился случайно, но так понравился, что решили оставить в статье именно эту диаграмму.
          0
          Это относительно популярный тип кривой силы света, так и называется «Batwing». В некоторых приложениях — очень актуально.
          0

          Поставил плюс чисто за концепт приложения.
          Вопрос по существу: скажите, а разве для таких задач не надо использовать алгоритмы из плеяды ray tracing или какой-нибудь фотонный маппинг, методы Монте-Карло итп?

            +1
            В принципе вы правы и для получения физически достоверного освещения именно так и нужжно поступать. Но в данном случае задача получается более упрощенной: Поскольку по госту есть требования минимальной освещенности, то достаточно подсчитать именно интенсивность прямого света, без учета переотраженния, что гарантированно обеспечит освещенность не ниже нормы.

            Естетсвенно, можно попытаться сэкономить на освещении и попробовать рассчитать все с учетом переотражений, но это для этого нужно учитывать множество параметров и характеристик каждого конкретного помещения, что конечно экономически не целесообразно и не возможно без дополнительных измерений
            0
            Спасибо за статью. Интересно почитать реализацию проекта, который мы так же оценивали на пред-продаже :)
              0
              Спасибо, интересно и полезно!
                0
                Эта прога — прям кусок моего диссера 8 летней давности ))). Жаль тогда приходилось многое писать ручками. Отличная реализация.
                  0
                  Bзвините, может я чего пропустил, но так и не понял почему не использовали DIALux?
                    0
                    Причин по которым было разработано собственное приложение несколько:
                    1) Цель любой компании, в первую очередь, привлекать клиентов именно к своей продукции. Одним из наиболее удобных способов является разработка собственных сервисов и услуг, позволяющих визуализировать возможности конкретной техники.
                    Конечно можно загрузить бесплатно базы и в Dialux, но там имеется большое множество светотехники различных производителей (порядка 400), а чтобы получить преференции в установке своих баз, производитель должен выплачивать определенную сумму ежегодно.
                    2) Dialux профессиональная программа, которая может быть достаточно сложна для освоения, и зачастую она излишняя для расчета определенного круга задач.
                    В этом случае удобнее использовать приложение, специально разработанное для этих целей.
                    3) У Dialux в данный момент отсутствует мобильная версия и веб-сервисы, поэтому проще и быстрее загрузить приложение Light-in-Night, и сделать расчет освещения на планшете.

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

                  Самое читаемое