Оптимизируем ассеты для WebGL правильно

Так часто бывает, что техники оптимизации, хорошо работающие для обычной десктопной или мобильной графики, не всегда дают нужный эффект в случае WebGL. В этой статье я собрал (а точнее перевёл на русский язык и изложил в текстовом виде нашу презентацию с Verge3Day) те методы повышения производительности, которые хорошо себя зарекомендовали при создании интерактивных веб-приложений.

f3-hpomrfalqm_dx6bd7wucnbgu.png

Геометрия / Меши


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

5pzqujnn_wdjuabotnmqswesfi8.jpeg

При моделировании складок лучше использовать шейдинг-группы, а не добавлять больше полигонов.

x3eh7ulhzw5kwlfz5ii6yys2jng.jpeg

При работе с цилиндрической моделью постарайтесь уменьшить количество полигонов ближе к центру.

an4en8ebhrlarhxwvxfco8-vx88.jpeg

Не перегружайте модель дополнительными деталями, которые пользователь все равно не увидит. Как показано на рисунке ниже, край, выделенный оранжевым цветом, определяет уровень детализации для всей модели, поэтому вы можете использовать его в качестве ориентира.

d9-pycf3wujunw3fuuryyotmfsa.jpeg

Карты нормалей


Распространенным способом оптимизации производительности WebGL является уменьшение количества полигонов путем создания карт нормалей.

cmllymx9s4rem9ynwyvywunmqxy.jpeg

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

cm1qrnqpt1qs13qi0oavun0l2ec.jpeg

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

ndbvuwvn3ao61dafaphqah7nhs0.jpeg

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

  • Ваш объект состоит из множества различных поверхностей.
  • У вас шероховатая поверхность, которая не дает видимых дефектов.
  • Ваши объекты такие отдаленные или маленькие, что пользователь не в состоянии заметить какие-либо артефакты.


b9-b1y8tfbf1yfs-vuss6vfuaky.jpeg

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


Вот типичный набор текстур, используемых в современной PBR модели освещения (и не только).

huip07zd2h1-gjr3pioqzmcnwxw.jpeg

Как видите, большинство из них черно-белые. Поэтому вы можете комбинировать ч/б текстуры в RGBA-каналах одного изображения (всего до 4-х карт на одно изображение).

bvhwcuec0dapzvsygiox7gpei2a.jpeg

Если у вас есть только одна ч/б текстура, вы можете объединить ее с любой существующей текстурой RGB, упаковав ее в альфа-канал. Наконец, если у вас нет изображения для объединения, вы можете преобразовать ваше черно-белое изображение в формат jpeg со сжатием 95% и включенным режимом оттенков серого.

jzwssyyopq_jswi7ahzny0zwhsa.jpeg

Еще один способ уменьшить размер текстуры — оптимизировать UV-развёртку. Чем компактнее ваша развертка, тем эффективнее ваше изображение будет использовать пространство текстуры. Это позволяет вам иметь более легковесные изображения без потери качества.

yv_kdhifbs63s5xai2om5z37avi.jpeg

Вертексные цвета


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

4j6w_xkffgfixggeghebye5tl_4.jpeg

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

ifdi-sbv5i300f7zkpjwie8cfd8.jpeg

Количество шейдеров


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

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

dm3wvs45kjv3deg4w0342os2o7e.jpeg

Вот пример такой оптимизации. Все четыре разновидности одной шины представлены одним материалом, и настраиваются путем замены текстур.

8l9dvgl_jlessslo7qozr7fnlfe.jpeg

Чтобы уменьшить количество шейдеров, вы можете объединить 2 или более простых материала в один более сложный. Этот метод особенно эффективен, если вы планируете переключаться между этими материалами (например, создаете приложение-конфигуратор), и не просто переключаться, а плавно и красиво анимировать переход от одного материала к другому.

tgqjnky59qyocifqwtgxtvotsae.jpeg

Draw-вызовы


Кроме того, есть еще один важный аспект — количество Draw-вызовов (они же draw calls и вызовы отрисовки). Это примерно соответствует количеству отдельных объектов, если для каждого объекта назначен только один материал, в то время как объектам с несколькими материалами требуется еще больше вызовов для их визуализации.

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

2s62ifvsueoudvxkdaa6o4ilwse.jpeg

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

rmsjwiqlei4hbedtbuafmxbrwsk.jpeg

HDR освещение


Это помогает значительно улучшить производительность, если вы освещаете свою сцену только с помощью изображения HDR, без использования каких-либо источников света. Файл HDR может весить менее 1 Мб.

pmj_rli9pkrhkwg4jgh0aggk5mu.jpeg

Тени


Используйте динамические тени только тогда, когда они помогают представить ваш объект в лучшем виде. На рисунке ниже показаны динамические тени из нашей демки Industrial Robot. Поскольку в этом приложении вращается сама модель, а не камера, тени не могут скрыть какую-либо часть объекта от пользователя и отлично подчёркивают форму робота. С другой стороны, такие же тени в демке Scooter будут затенять многие детали модели.

o4pxikw8wlgmidid3mlswai50bq.jpeg

Отсюда мы делаем вывод: если ваш объект не перемещается в приложении, вы можете запечь карты теней и ambient occlusion и назначить их на плоскость, расположенную под объектом. Такое решение будет более производительным и выглядеть качественнее, чем при использовании динамических теней.

a45dj1fnhbxpi4u8tnrpmqaptai.jpeg

На этом всё. Если у вас есть ещё какие-нибудь советы, которые могут помочь с производительностью WebGL, пишите в комментариях.

© Habrahabr.ru