Найти в Дзене
JUST GAME DEV

Оптимизация 3D графики в UNITY

Оглавление

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

1. Следить за количеством полигонов

Вы должны оптимально выбирать кол-во полигонов для объектов. Если объект на экране занимает малую площадь на экране, то не стоит тратить слишком много ресурсов для его модельки. Стоит подчеркнуть детали у более стоящих объектов. А если вам нужно чтобы каждый объект выглядел хорошо, то стоит использовать LOD'ы (Level of detail). На простом языке, это просто несколько вариантов одной модельки с разными уровнями детализации. И при приближении камеры просто включать нужный вариант модельки или совсем отключать. В Unity уже есть стандартный компонент, который реализует эту систему.

LOD Group - стандартный компонент UNITY для создании и настройки групп уровней детализации.
LOD Group - стандартный компонент UNITY для создании и настройки групп уровней детализации.

2. Разрешение текстур

Тут все просто, чем меньше размер текстур, тем больше производительность, но меньше детализации. Вы также как и с количеством полигонов должны выставлять приоритеты, а также сделать несколько вариантов модельки с разными разрешениями текстур и объединить их в одну LOD группу. Также очень важно, чтобы разрешение текстур было равно двум в степени. Так как устройство будет выделять память под это кол-во. Например: если у вас текстура разрешением 825x513, то устройство выделит памяти как для текстуры 1024x1024, даже если размер текстуры по одной из сторон превышает хотя бы на один пиксель. Текстуру лучше делать максимального размера, а уже в редакторе UNITY менять ее максимальный размер(max size) в настройках импорта текстур.

Настройки размера и сжатия текстур.
Настройки размера и сжатия текстур.

3. Оптимальной использование UV развертки

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

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

4. Использование галочки static

У каждого объекта в Unity есть параметр static.

Static параметр у объектов.
Static параметр у объектов.

Включайте этот параметр на неподвижных объектах, это даст очень большой прирост производительности для отрисовки этих объектов. Связанно это с батчингом(batching). Это процесс объединение нескольких полных отрисовок экрана в один. Unity пытается оптимизировать процессы отрисовки и пытается отрисовать несколько объектов за один drawcall(вызов отрисовки), а пометив объекты static вы облегчите ему эту задачу. Но по законам вселенной если вы в чем то выиграли, значит вы в чем-то проиграли. Дело в том, что статичный батчинг увеличивает размеры требуемой памяти, поэтому не стоит этим злоупотреблять. Иногда можно просто объединить несколько моделей в одну на процессе моделирования, это тоже поможет помочь, но если только материал у это общей модели будет один.

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

В следующем пункте я опишу, как помочь Unity с батчингом динамических объектов.

5. Батчинг динамических объектов

Unity может батчить не только статичные объекты, но и динамические. Все 3D модели отрисовываются в основном с помощью одного из двух компонентов: Mesh renderer, Skinned mesh renderer. Skinned mesh'ы используются для костных мешей, они между собой не батчатся. А чтобы Unity мог батчить обычные мешы, они должны иметь один и тот же материал. Если у вас будут разные текстуры, но один материал на нескольких объектах, можно расположить развертку этих объектов на одной карте развертки и сделать так называемый атлас текстур, когда в одном изображении лежат текстуры от нескольких моделек.

Иногда нужно на время изменить параметры материла у одного объекта на время, а потом вернуть. Например: при попадании враг на несколько секунд становится красным, а потом возвращается в обратное состояние. Проблема тут в том, что при обращении к полю material у компонента MeshRenderer он возвращает новый экземпляр материал, и при назначение этого материала это уже будет другой материал и динамический батчинг не будет работать, а если обратится к полю sharedMaterial и измените его, то материал изменится на всех модельках сразу. Я использую такой способ: просто запоминаю sharedMaterial на меше в локальную переменную. Произвожу нужные мне операции с материалом, а после просто назначаю в поле material уже запомненный sharedMaterial.

Пример кода покраснения врага.
Пример кода покраснения врага.

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

6. Occlusion culling

В рендеринге есть такое понятие как overdraw. Это процесс, когда один и тот же участок экрана отрисовывается несколько раз. Например у вас на сцене расположена камера, которая смотрит на камень. А за этим камне лежит другой камень, но уже поменьше и из камеры его не видно, но игровой движок, все равно отрисовывает сначала маленький камень, а затем уже большой. Это процесс бесполезной работы называется overdraw. Occlusion culling это функция, которая отключает рендеринг ненужных объектов. Почитать как ее использовать можно здесь: Руководство: Occlusion culling.

7. Z-buffer

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

Пару советов

  • Иногда не стоит в начале слишком сильно упиваться оптимизацией, так вы потеряете кучу времени и сил, а результат вас может не порадовать. Иногда лучше сначала сделать простую версию без оптимизации, а потом уже масштабировать и думать о производительности.
  • Чтобы посмотреть как отрисовывается кадр в Unity, вы можете зайти в окно frame debugger(Windows>Analysis>Frame debugger) в play mode и посмотреть, что больше всего занимает в процессе отрисовки.

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