КАК РАБОТАЕТ ОПТИМИЗАЦИЯ ИГР?

Как оптимизируют графику в играх

В 2021 году в рамках онлайн-сессии From Zero to Game на азербайджанском портале Gamepons программист Васиф Абдуллаев затронул тему оптимизации графики в видеоиграх с позиции разработчика. В рамках своего выступления он объяснил, что такое оптимизация, как разработчики оптимизируют графику в играх, зачем они это делают и каких усилий им это стоит.

Более пяти лет занимается разработкой мобильных и десктопных игр в небольших инди-командах и крупных студиях. Он специализируется на программировании геймплея, шейдеров компьютерной графики и мультиплеера в движках Unity и Unreal Engine. Сейчас сотрудничает с турецкой студией Coconut Game.

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

В разработке игр выделяют две категории оптимизации:

Примеры игр с хорошей оптимизацией:

Скриншот: игра Metal Gear Solid V: The Phantom Pain / Konami Digital Entertainment

Скриншот: игра Marvel’s Spider-Man Remastered / Insomniac Games

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

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

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

Vulkan и DirectX 12 можно отнести к «современной» категории интерфейсов. Мы привыкли считать, что понятие «современный» подразумевает более упрощённый подход. Однако код рендеринга для простого треугольника в Vulkan занимает около 1000 строк, а в OpenGL — менее 100.

Рендеринг простого треугольника в OpenGLКадр: Gamepons / YouTube

Всё дело в том, что такой тип API не прячет все данные в функции, а даёт разработчику гибкость для оптимизации, особенно в отношении CPU.

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

Дроуколлы в кодеКадр: Gamepons / YouTube

К слову, шейдер — это тоже тип программы для рендеринга, которая определяет итоговый вид поверхности объекта в сцене: наложение текстур, рельефность, взаимодействие со светом (поглощение, рассеивание, отражение, преломление и так далее). Шейдеры взаимодействуют с графическим ускорителем и написаны в основном на языках HLSL или GLSL. Но в большинстве случаев разработчики не взаимодействуют с кодом шейдеров напрямую, а настраивают их в движке. Например, шейдеры в Unreal Engine можно собрать с помощью нодов во встроенном редакторе.

Визуальное программирование шейдеров в Unreal Engine 5Изображение: Unreal Engine 5

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

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

Следует помнить, что идеального игрового движка не существует. Даже проекты на нашумевшем Unreal Engine 5 нуждаются в оптимизации по нескольким причинам:

Рендеринг, или отрисовка изображения, производится с помощью вычислений центрального процессора. В результате игрок видит на экране набор кадров или анимацию 3D-объектов с учётом их расположения, текстурирования, освещения и других характеристик. Различают однопоточный рендеринг с синхронным воспроизведением вычислений и многопоточный, во время которого задействовано несколько ядер процессора. Во втором случае нагрузка распределяется равномерно.

Есть два метода рендеринга на графическом процессоре — с помощью трассировки лучей и с помощью растеризации 3D-моделей. Последний более распространён, поэтому его стоит рассмотреть подробнее.

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

Краткое описание стадий, обозначенных на схеме выше:

Этапы растеризации могут варьироваться в зависимости от архитектуры графического процессора. Более подробно обо всех стадиях (в контексте Direct3D) можно прочесть в официальной справке Microsoft.

Один из важных аспектов оптимизации — использование профайлеров. Это инструменты, с помощью которых можно получить информацию об элементах, снижающих производительность в конкретных сценах. Также профайлер отображает работу низкоуровневых модулей API. В отличие от общепринятых стандартов, инструмент измеряет производительность в миллисекундах (ms), а не в частоте кадров в секунду.

Профилировать графику можно непосредственно в движке (Unreal Insights для Unreal Engine, Unity Profiler для Unity) или с помощью сторонних утилит. Среди последних наиболее известны RenderDoc, NVIDIA Nsight, Radeon GPU Profiler и Pix.

Пример работы NVIDIA NsightКадр: Gamepons / YouTube

Работая с профайлером, следует обращать внимание на несколько моментов:

Нет смысла размещать объекты с высокой детализацией на дальнем расстоянии, так как игрок их всё равно не увидит. К тому же это приведёт к снижению производительности из-за овершейдинга мелких деталей. Модели с низким уровнем детализации не нуждаются в освещении — для них достаточно unlit-материалов. Для текстурирования в этом случае нужно использовать только карту альбедо в низком разрешении.

Всегда подключайте MIP-мэппинг в настройках текстур. М IP-мэппинг создаёт несколько копий одной текстуры с разной детализацией. Такой подход сводит полосы от швов на моделях к минимуму и предотвращает возможные проблемы в результате сглаживания.

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

Пример установки текстур в Unreal EngineКадр: Gamepons / YouTube

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

Размещение текстур из атласа на нескольких мешахИзображение: Ivan Spalla / Highend3D

Оригинальная модель и модель после применения атласаКадр: Ivan Spalla / Vimeo

Текстурный атласКадр: Ivan Spalla / Vimeo

Упреждающий рендеринг (Forward Rendering) — стандартный метод рендеринга «из коробки», который используется в большинстве движков. G PU получает данные о геометрии, проецирует её и разбивает на вершины. Затем происходит преобразование вершин в геометрию и дальнейшее её разделение на фрагменты или пиксели. Они подвергаются финальной отрисовке, после чего передаются на экран. При этом освещение в сцене вычисляется отдельно для каждой вершины и каждого фрагмента в зоне видимости с учётом количества источников света.

При отложенном рендеринге (Deferred Rendering) сначала отрисовывается вся геометрия. Затем на полученный результат накладывают фрагментарные шейдеры с освещением, и только после этого происходит вывод картинки на экран.

Иногда под отложенным рендерингом подразумевают отложенное освещение (Deferred Shading / Lighting) — модификацию отложенного рендеринга, которая уменьшает размер за счёт большого количества источников света в кадре.

Почему важно обращать внимание на метод отрисовки при оптимизации? Допустим, в сцене 100 объектов, в каждом из них порядка 1000 вершин. В таком случае в кадре будет 100 000 полигонов, которые легко обработает видеокарта. Но когда эти полигоны попадают на стадию фрагментного шейдера, запускается процесс вычисления освещения в сцене. А так как при стандартном методе расчёты освещения ведутся по отдельности для каждого сегмента, при выводе изображения не исключены задержки.

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

Например, в Unreal Engine 5 этот тип рендеринга используется по умолчанию. Однако у него есть свои недостатки — в частности, несовместимость с некоторыми платформами. Поэтому выбор метода рендеринга в пользу оптимизации зависит от задач разработчика и специфики игрового движка.

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

При настройках усечения обращайте внимание на минимальный размер окклюдера. Окклюдеры — это объекты, позволяющие отсекать геометрию, которая находится за ними. Например, на скриншоте ниже выставлено значение 5. Это значит, что все объекты выше или шире 5 метров не будут отображаться позади окклюдера таких объёмов, что экономит время рендеринга. Размер окклюдера во многом зависит от геймдизайна игры.

Настройки окклюдера в движке UnityИзображение: Unity

Если речь идёт об усечении динамических мешей, в этом случае стоит использовать пул объектов, то есть повторно применять сущности вместо их создания и уничтожения.

 

Игрок не сможет детально рассмотреть объекты малого размера, расположенные на дальнем расстоянии. Поэтому в движке настраивают дистанцию усечения, исходя из размеров мешей. В Unity это делают с помощью кода, а в Unreal Engine — с помощью инструмента Cull Distance Volumes.

Многие анимации и визуальные эффекты на основе физики нагружают процессор. Вместо этого можно воспользоваться менее ресурсоёмкими техниками. Например, при вертексной анимации, известной как Vertex Animation Texture, используются только текстуры и шейдеры, поэтому основная нагрузка идёт на видеокарту, а не на процессор.

Демонстрация вертексной анимации GIF: лекция Васифа Абдуллаева на Gamepons / YouTube

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

Также следует воздержаться от большого количества конструкций ветвления (if) в коде шейдеров. G PU лучше воспринимает параллельный код.

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

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

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

Жизнь можно сделать лучше!Освойте востребованную профессию, зарабатывайте больше и получайте от работы удовольствие. А мы поможем с трудоустройством и важными для работодателей навыками.

Многие начинающие 3D-художники и разработчики игр знают, что существует «оптимизация» — нечто, что позволяет повысить частоту кадров. Но зачастую они не разбираются в том, как она работает — и это заставляет их принимать неверные решения.

В чём проблема

Работая с геометрией, новичок может потратить уйму времени впустую, пытаясь сократить количество полигонов. Даже если лимит по техническому заданию позволяет определённому LOD (уровню детализации) держать в себе 10-15 тысяч трисов (полигонов с тремя сторонами), моделер может решить, что если он всё уместит в 5 тысяч треугольников, то работа станет более «оптимизированной».

Оценить абстрактную «стоимость» рендера одного кадра невозможно — нужно знать, под какие устройства создаётся игра, и что именно в ней отрисовывается.

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

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

Как устроена отрисовка кадра

Процесс отрисовки кадра — это та самая «магия под капотом движка». У отрисовки, как и у работы художника, есть стоимость, и рассчитывается она исходя из необходимого времени.

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

Во всех движках есть вывод статистики. Самое важное в нашем случае — это значение времени одного кадра.​

«Время кадра» — это время в милисекундах, за которое может быть построен и отрисован один кадр. Чтобы получить 60 кадров в секунду, один кадр должен быть обсчитан за 16 милисекунд или еще быстрее.

Draw Call (вызов отрисовки) — это одна графическая команда, которая должна что то отрисовать.

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

И наоборот — чем меньше вызовов отрисовки, тем быстрее считается кадр, и тем выше FPS.

Frame Debugger — встроенная утилита Unity, позволяющая прямо в редакторе разобрать один кадр на все составляющие. Этот кадр занял 96 отрисовок.​

Как устроен батчинг

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

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

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

Например, тысячи рыб в кадре в Abzu — это на самом деле партикли, геометрия которых дополнительно анимирована шейдером с вертексной анимацией.

Статический же батчинг происходит «заранее». Для этого нужно указать что этот объект статичен, и никогда не будет изменен или сдвинут.

Отличие от динамического батчинга в том, что он позволяет перерабатывать огромное количество объектов. За один батч Unity может сшить объектов общим количеством до 64 000 треугольников.

Кроме того, динамический батчинг будет пытаться пересобрать геометрию почти в каждом новом кадре, — а значит будет постоянно увеличивать время расчёта.

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

Чтобы батчинг сработал, объекты должны быть «одинаковы». Объекты должны иметь один и тот же шейдер, материал на этом шейдере (в Unreal это инстанс материала), текстуру и остальные параметры объекта, а также не должны иметь Non Uniform Scale и не должны быть разбиты светом.

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

​Пример очень простого атласа для тайловых поверхностей.

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

Однако батчинг тоже не «бесплатен» — даже если он статический и обсчитан заранее. Сцены в играх нужно освещать, и даже в случае статичного освещения это будет влиять на батчинг.

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

Освещение в реальном времени тоже будет влиять на то, какие объекты сбатчатся, а какие — нет.

Световые зонды (Light Probes) нужны, чтобы запечь в себя шейдинг в статичном освещении. Они позволяют затенить динамический объект в сцене с Light Map.​

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

Поэтому иногда применяют «ручной» батчинг, он же просто «мердж» (merge). Берём геометрию, и в любой программе сшиваем так, как нужно под конкретную сцену.

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

Иногда гораздо выгоднее скормить движку огромный меш в 64 тысячи треугольников, чтобы отрисовать всю локацию целиком. Так поступили, например, разработчики Guns Of Boom.

Подобный подход практически полностью исключает использование Occlusion Culling. Эта техника позволяет не рисовать те объекты, которые не видны камере. То есть, когда ящик перекрыт другим более крупным ящиком — мы его не видим, а значит можем не рисовать. В случае со «склеенными» мешами такое разделение невозможно.

Level of Detail (LOD, или уровень детализации) — простая техника, которая позволяет уместить огромное количество объектов в кадре, не отнимая время на обработку лишней геометрии.

LOD может разбить объекты на разные батчи: и в этом нет ничего страшного, ведь зачастую несколько сотен лишних DrawCall будет быстрее обсчитаны на лодированых мешах чем рисовать их оригиналы — но сбатченные.

Сейчас есть неплохие инструменты автоматической генерации LOD-мешей, и это облегчает работу. При нужной настройке можно выдать результат не сильно хуже ручной ретопологии. Но даже используя автоматику, не стоит делать тысячи разных уровней детализации: все уровни LOD тоже нужно хранить в памяти, а её не стоит забивать просто так.

Обычно используют четыре уровня детализации. L OD-0 — это оригинальная модель, а LOD-3 — дальняя. L OD4 может быть просто 2D спрайтом который всегда смотрит на игрока.

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

Это касается даже мелких объектов: например, у обычной кружки в одной из стартовых сцен Dead Space 2 было достаточно полигонов, чтобы она не казалась «квадратной», в то время как в остальной игре в LOD-0 у аналогичных объектов не было такого сглаживания.

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

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

Подход, основанный на роли модели в кадре полностью защищает вас от критики сторонников «идеальной сетки», которые без понимания контекста — просто по скриншоту, — заявят, что вы сделали 10 лишних треугольников.

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

Рендер-пайплайн должен адекватно рассортировать и нарисовать сцену — обработать шейдеры, геометрию, текстуры и пост-процессы. К тому же он должен учитывать, что объекты могут быть прозрачными.

Чем больше площадь прозрачности в кадре, и чем больше прозрачные объекты наслаиваются друг на друга — тем дольше рендер-пайплайн будет рисовать итоговый пиксель, ведь ему придётся каждый раз его перерисовывать. Это называется Alpha overdraw.

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

Cлева — обычный «плейн» с текстурой ветки. Справа — он же, но силуэт ветки обрезан по контуру, чтобы срезать лишнюю прозрачную площадь.​

Z-Fight — артефакт, который образуется, если поставить несколько полигонов в одной плоскости. Рендеру не хватает точности глубины, чтобы отсортировать отрисовку этих полигонов в правильном порядке.

Но кроме визуального артефакта, который заметит игрок, это повлияет и на время рендера — ведь пиксели в этом месте будут постоянно «спорить» друг с другом об очереди отрисовки.

Два плейна на одной оси. Смешение цветов по центру и есть Z-Fight.​

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

Нынешнее железо — даже мобильное, — может обрабатывать огромное количество треугольников. Объёмы памяти и скорость растут. Чипы на мобильных устройствах и их батареи, которые перегреваются при высоком FPS, тоже становятся всё совершеннее.

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

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

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

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

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

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

Среди других преподавателей — Михаил Кадиков (Crytek), Макс Пирс (CD Projekt Red), Елена Кондрашина (Mundfish) и Илья Иванов (CD Projekt Red).

Как создавать игры, которые не тормозят

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

vlada_maestro / shutterstock

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

Мы живём в эпоху невероятно мощных компьютеров. У нас есть видеокарты, которые позволяют трассировать лучи в реальном времени, хотя многие считали, что до этой технологии нам как до Луны.

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

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

В статье про гонку на Unity я использовал генерируемую дорогу, чтобы сделать игру бесконечной. Для этого перед игроком добавлялись новые дорожные блоки, а когда игрок проезжал определённое расстояние от блока, тот удалялся.

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

Даже если локация не генерируется, всё равно найдётся что удалить:

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

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

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

Тут можно либо сделать количество объектов минимальным, либо использовать «грязные» трюки, как это сделали разработчики игры Super Time Force.

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

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

Но вот с физикой всё же нужно быть осторожнее. Если игрок отвернётся от объекта, пока тот падал, а вы его (объект) отключите, то получится странный эффект. Объект никогда не упадёт, пока игрок снова на него не посмотрит.

Впрочем, это можно использовать как особенность игры. Хороший пример: в сериале «Доктор Кто» есть существа, называемые плачущими ангелами. На вид это обычные статуи, но, когда на них никто не смотрит, они могут перемещаться.

Отсылка к этим ангелам есть в игре The Witcher 3:

Тут можно подойти по-разному:

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

В GTA: Vice City кусты всё время были обычными текстурами

Всегда старайтесь использовать ассеты, которые меньше весят: то есть low-poly (англ. низкополигональные) объекты, сжатые текстуры и так далее. Как правило, почти любой ассет можно оптимизировать так, что он по-прежнему будет хорошо выглядеть. И если с текстурами вы можете сделать это сами, то оптимизировать объекты может только опытный 3D-художник.

Вот пример неплохой низкополигональной (по современным меркам) модели:

Если добавить хорошую текстуру, то такая модель подойдёт в качестве декорации для проекта с хорошей графикой:

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

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

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

Что касается теней, то тут лучше всего поможет ограничение числа объектов с динамичными тенями.

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

Как это сделать, зависит от выбранной технологии.

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

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

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

Поэтому ещё во время разработки нужно определиться, для каких устройств и для какой аудитории будет ваша игра. Возможно, простая графика и низкие требования к железу станут вашими фишками?

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

Твой ПуКа надо каждые два года обновлять, а я вот на своей PS4 купленной на старте спокойно играю в РДР2 и кайфую, потому что консоли оптимизированы специально для игр»

Нормис-консольщик с ПДФ

Разрешение и fps

Вы чего-то не увидели, да? Правильно, вы не увидели ни слова про графику. А почему? Потому что настройки графики (дальность прорисовки, отражения, тесселяция и прочее) ставятся и настраиваются уже тогда, когда основные параметры достигнуты. Возьмем AC Valhalla — вышла на релизе PS5, целевые 1440р и 60 фпс достигнуты легко, поэтому там без проблем впихнули высокие-ультры. Возьмем RDR 2 на базовой PS4. Там 1080р и 30 фпс были достигнуты, однако мощностей приставки не хватило для хорошей графики, поэтому там есть настройки ниже «минималок» на ПК. В общем, запомните, Ультры и высокие настройки графики — это не про консоли. В основном там будут средние либо средне-низкие.

Динамическое разрешение и падение fps

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

Динамическое разрешение подразумевает снижение рендера картинки в моменты, когда игра не может вывезти стабильные 30/60 фпс. В игре вы снижение в замесе с 1080р до 900р не заметите. Но оно бывает даже в эксклюзивах (например, в Spider-Man 2018).

ФПС может падать ниже целевых 60 и 30. Причем, это также происходит во многих проектах. В некоторых проектах фпс в принципе «плавает», из недавних, навскидку, — Elden Ring и грядущий RE4 (на PS4).

Откуда миф про магию оптимизации?

Его очень просто объяснить. На самом деле, все сводится к тому, что на консолях, банально, нет настроек графики. Сразу вспоминается Ghostwire Tokyo, где было, по-моему, аж 6 пресетов графики, и люди обсуждали, где играбельно, а где нет. Или вспоминается Киберпанк на PS5, где также люди обсуждали что в режиме 30 фпс с лучами неиграбельно из-за большого инпут-лага. То есть, стоило в новом поколении ввести пресеты графики и уже начали возникать сложности.

Когда человек покупает компьютер, он очень часто (особенно если у него хорошая видеокарточка) врубает «ультры». И видит, что его ПК не тянет в стабильные 60 фпс. А поставить 30 он не может, потому что выглядит неплавно. Есть такой термин — FOMO, или же страх упустить что-то лучшее. Человек действительно хочет получить максимальный опыт, и это нормально. Но при этом, видя на своей 1060 в РДР2 при 1080р нестабильные 60 фпс он расстраивается, что объяснимо. Если вы посмотрите Digital Foundry, то увидите что консольные игры — это смесь средних настроек, с динамическим разрешением и (не всегда) 60 фпс. Типичные, средние настройки, не более.

Запустил на плойке все нормально идет 4К в 60 фпс сижу играю, а на ПК на ультрах все тормозит, нафиг надо

Отсутствие настроек графики играет на руку продавцам консолей. Я вообще удивлен, зачем они решили дать пресеты качество/производительность. Половина консольщиков искренне верит, что PS5 запускает игры в 4К, раз на коробке написано 4К (я не шучу сейчас), и многие действительно считают что такую графику на ПК никогда не увидеть. Многие играют в 30 фпс и горя не знают. Помните мем, что «глаз консольщика больше 24 кадров не видит». Это сейчас смешно, а года 3-4 назад в обсуждениях на полном серьезе люди утверждали, что 30 фпс на консолях равны 60-ти на ПК, и «ощущаются» лучше.

Доступна ли магия оптимизации на ПК?

Конечно. Для того чтобы оптимизировать Ваш ПК под PS5 и получить консольный опыт, Вам достаточно иметь видеокарту уровня 2070 (3060). Ставите 1440р (можно даже с ДЛСС, разницы не увидите), средние настройки (не бойтесь ставить все на среднее, кроме текстур, их на ультра), и ставите лок на 60 (ну или 30) фпс. Поздравляю, вы оптимизировали игру.

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

Выше железа не прыгнешь. Вся консольная оптимизация — это урезание настроек, снижение разрешения и фпс. Того же самого вы можете добиться и на ПК.

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

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

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

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

Когда начинается процесс оптимизации?

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

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

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

Впрочем, это всё общие вопросы. Основная работа по оптимизации начинается во время разработки игры. И вот тут есть три подхода к разработке:

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

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

Основные конфликты оптимизации

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

Оптимизация — это всегда поиск баланса между двумя противоположностями, каждая из которых работает только в конкретных случаях. Где-то подойдёт одно решение, где-то противоположное. Эти противоположности я и перечислю:

Данные — вычисления

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

При желании, конечно можно всю игру генерировать в реальном времени, но вообще чем комплекснее задумки авторов — тем больше приходится тратить на них процессорных мощностей, и не всем игрокам эти мощности доступны. В некоторых (не во всех) случаях можно сохранить вычисления заранее (это называется «запечь» или «bake») и использовать их позднее. Например, запечь освещение в текстуры, сохранить результаты столкновений и повреждений в анимационных клипах, составить модели поведения, сделать пререндер катсцен и прочее. Запекание обеспечит максимальное качество при минимальных требованиях к мощностям.

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

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

Использование дополнительных инструментов

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

В эту область входят и различные игровые движки (Unity, Unreal Engine, CryEngine), и инструменты для арт-отдела (Blender, Houdini, Substance Painter), и физические подсистемы (PhysX, Havok, Box2D), и графические технологии (рейтрейсинг, постпроцессинг, тесселяция), и различные библиотеки для сетевых взаимодействий, сбора аналитики, устройств управления, звукового сопровождения, хранения внутриигровых данных и так далее, далее, далее.

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

И, скажем честно, эти инструменты улучшают жизнь больше игрокам, чем разработчикам. Эти технологии хоть и подают себя как «продукт, готовый к использованию», на деле это каша из большого количества различных не очень стабильных разработок, пытающихся одинаково удовлетворить потребности как можно большего количества клиентов.

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

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

Качество кода и его работоспособность

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

Для начала, интересная корреляция: чем быстрее программы, написанные на языке, тем сложнее им пользоваться. Python может выучить любой желающий, С++ до сих пор остаётся одним из самых сложных для понимания среди С-подобных языков, в то же время по некоторым тестам разница в производительности между ними отличается в 80 раз в пользу второго.

Если говорить о программах, то программистам приходится выбирать между производительностью и стабильностью. Например, различные проверки корректности входящих данных (самая частая — проверка на null) и обработчики исключений могут занимать вплоть до нескольких процентов мощностей, 99% времени они излишни. В то же время, без этих проверок программисту намного труднее найти и исправить потенциальную ошибку.

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

Согласно  MVC, вот такое нагромождение из трёх классов и четырёх интерфейсов нужно конструировать для каждой отдельной программной сущности. По моим оценкам, такой подход замедляет разработку на начальных этапах вплоть до 50%

Это уж я не говорю о различных хаках (в том числе ассемблерных) и тяжёлых математических алгоритмах, заметно ускоряющих игру, но превращающих код в нечитаемый набор букв.

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

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

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

В чём заключается процесс оптимизации

Оптимизация, как вы уже поняли, затрагивает все компоненты игры. Хотя все эти компоненты неразрывно связаны, условно их можно поделить на следующие группы:

Тут всё понятно: в игре есть разные виды ассетов, как например текстуры, модели, звуки, анимации, и чем меньше они по объёму — тем быстрее компьютер будет их обрабатывать (в первую очередь тут имеется в виду скорость загрузки ассетов в память и на игровой уровень).

Схитрить через программный код тут особо не получится: контроль ресурсов полностью на стороне движка и железа. Поэтому кроме как уменьшать объём ассетов и многократно их переиспользовать, выхода особого и нет. Не требовать же поголовного использования SSD?

Создавать сами ассеты не очень тяжело, особенно сейчас: есть очень много удобных инструментов создания ассетов для любых задач, даже бесплатных. Однако эти инструменты очень расточительны. На примере 3D-моделей, легко слепить модель с помощью скульптора и автоматически сгенерировать для неё текстурную UV-развёртку, но в ней будут тысячи лишних (мало влияющих на качество отображения) точек, а процент используемого развёрткой пространства будет в районе 40-60%, что потребует использования текстур более высокого разрешения.

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

Количество полигонов в модели уменьшилось в 15 раз, а визуально разница почти не заметна

Оптимизация программных компонентов

Намеренно не пишу «код», потому что чаще всего дело не в коде, если вы его, конечно, более-менее умеете писать. Но оптимизация кода тоже входит в этот пункт.

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

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

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

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

Для начала, можно поискать задачи, которые не требуют выполнения в каждом кадре, а, например, достаточно одного раза в секунду, как проверка доступности сети. Если у нас алгоритмы поиска (пусть будет поиск пути), мы можем добавить упрощений, которые будут заранее сужать пространство перебора. Где-то можно понизить точность вычислений. Для каждой задачи свои решения.

Александр

Здравствуйте, меня зовут Александр, уже более 10 лет я занимаюсь ремонтом компьютером, этот сайт я создал чтобы делиться полезной и практической информацией с вами! Буду благодарен, если вы опишите свой опыт или мнение в комментарии, надеюсь, что данная информация принесёт только пользу

Оцените автора
WindowsComp.ru
Добавить комментарий