Почему графику в видеоиграх по-прежнему так сложно создавать?

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!


Введение


В этом посте будут рассмотрены трудности и аспекты производства, которые нужно учитывать при создании новых методик и алгоритмов рендеринга/графики, особенно в контексте прикладных исследований рендеринга реального времени. Я буду рассказывать о своём личном опыте работы над Witcher 2, Assassin’s Creed 4: Black Flag, Far Cry 4 и God of War.

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

Я слышал вопросы типа «почему эта великолепная исследовательская методика X не используется в продакшене?» и от геймеров, и от коллег из научных кругов. На то всегда бывают очень веские причины!

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

Я вспоминаю об этой теме ещё и каждый раз, когда слышу дискуссии о том, что «фотограмметрия, трассировка лучей, нейронный рендеринг, [вставить любую другую новую тему] станет универсальным решением для рендеринга и заменит всё остальное!». Спойлер: этого не случится (по крайней мере, в ближайшее время).

Целевая аудитория


Целевая аудитория моего поста:

  • Студенты, изучающие компьютерную графику, и прикладные исследователи,
  • Инженеры рендеринга, особенно начинающие свою карьеру, которые ещё не выработали интуитивного понимания,
  • Технические художники и руководители/продюсеры арт-отделов,
  • Технические директоры и ответственные лица, не имеющие опыта в графике,
  • Инженеры и архитекторы оборудования, работающие над тем, что связано с GPU или с графикой (которым любопытно, почему сложно использовать новые аппаратные функции),
  • Люди, которые любят игровую графику и интересуются ею (или графикой реального времени в целом), желающие чуть больше разобраться в том, «как делается колбаса». Некоторые концепции могут показаться слишком техническими или в них может быть слишком много жаргона, но их спокойно можно пропустить.

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

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

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


Очень важное замечание — ни одно из описанных в статье «препятствий» на самом деле не является причиной не использовать методику.

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

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

Способы применения


Категории «способов применения» заслуживают отдельного объяснения и описания «серьёзности» их ограничений.

Технологическое демо


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

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

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

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

Специальный уровень/однократное использование


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

Примером может служить освещение на уровнях с джунглями в Assassin’s Creed 4: Black Flag, над которым я работал.


Источник: рекламные материалы Assassin’s Creed 4: Black Flag. Обратите внимание на столбы света с подобием каустики, которые были важным элементом рендеринга на уровнях с джунглями и позволили нам много сэкономить на рендеринге теней!

Джунгли были «отрезаны» от остальной части открытого мира специальными потоковыми коридорами, и в них мы полностью заменяли всю модель освещения! Вместо древоподобных карт теней и глобального освещения мы создали фальшивую «каустику», которая выглядела намного мягче и хорошо сочеталась с нашей системой объёмного освещения/атмосферных явлений. Она не только выглядела лучше, но и была гораздо быстрее. Очевидно, что она работала только благодаря особым условиям.

Кинематографические вставки


Чуть более требовательный тип функций — кинематографический. Кинематографические вставки слегка отличаются тем, что их очень сильно могут контролировать художники и большинство их аспектов (освещение, расположение персонажей и анимации) фальшивы — прямо как в настоящем кино! Кинематографические вставки часто содержат быстрые смены кадра (полезно для сокрытия переходов/рывков) и имеют больший бюджет вычислительных ресурсов благодаря своей предсказуемости (и даже в консольных играх с 60 fps рендерятся в 30 fps).


В кинематографических вставках Witcher 2 увеличена детализация персонажей, добавлено красивое реалистичное радиальное боке и уникальное освещение. Однако заметьте, что в них рендерится очень мало объектов!

Обычные функции рендеринга


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

В моём посте в основном будет рассматриваться эта категория.

Ключевые/уникальные функции


Как ни парадоксально, если что-то является ключевой или уникальной функцией, это может облегчить многие трудности. Возьмём для примера VR — стереозвук, производительность (низкие задержки) и идеальное сглаживание AA без размазывания (так что забудьте о TAA) являются главными функциями, абсолютно необходимыми для ядра игрового процесса. Это означает, что вы можете, например, игнорировать рендеринг растительности или какие-то анимации, которые бы выглядели нереалистично: самое важное — это погружение и ощущения от VR!

Совместимость функций


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

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

Плотная геометрия


Плотная геометрия, например, растительность (которая и вдохновила меня на создание поста) — враг большинства алгоритмов рендеринга.

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

Ранее отсечение по глубине и перекрытию? Реализовать очень сложно.

Отделение поверхностей от объёмов? Очень сложно.

Хранение информации для каждой уникальной параметризации поверхностей? Почти невозможно.

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


Растительность в Witcher 2 — какая плотность! На мой взгляд, это по-прежнему один из лучших примеров леса в играх.

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

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

Альфа-тестирование


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

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

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

Также это сильно усложняет сглаживание (забудьте об обычном MSAA, необходимо будет эмулировать alpha to coverage).

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

Анимация — скелетная


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

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

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


Требюше из Witcher 2 — это не люди, но у них всё равно есть «скелеты» и «кости», и в них также используется скелетная анимация!

Анимация — процедурная и нежёсткая


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

См. это видео про движение в промежуточном ПО Speedtree — всё движение здесь описывается математическими формулами, а не анимировано вручную, и выглядит фантастически и реалистично.

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

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

Анимация — текстуры


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


Дождь в Assassin’s Creed 4 основан на смещении нормалей с процедурно генерируемой картой высот и текстурой дождя. См. мой доклад на Digital Dragons о рендеринге дождя в игре.

Динамическая точка обзора


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

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

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

Динамическое освещение


Насколько динамично освещение? Есть ли в игре динамическая смена дня и ночи? Система погоды? Может ли пользователь включать/выключать освещение? Испускают ли спецэффекты свет? Есть ли объекты, испускающие динамические тени?

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

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

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

Тени


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

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

Для частиц и объёмов ответ может быть непростым (так как частичная пропускаемость не поддерживается картами теней), но и «простые» методики наподобие тесселяции мешей или параллаксных карт перекрытия тоже будут создавать несоответствие между испускающим и получающим тень объектами, что потенциально может вызывать артефакты теней!


Слайд из моего доклада на GDC 2014, демонстрирующий параллаксную карту перекрытия в AC4 — обратите внимание на полное отсутствие теней.

Динамическое окружение


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

В God of War участок карты Caldera Lake и мост, движущиеся уровни воды и повороты моста были одними из самых серьёзных проблем на протяжении всего продакшена с точки зрения систем освещения/глобального освещения, которые используют предварительные вычисления. Не существовало какого-то «общего» решения, всё зависело от ручной работы и потоковой передач загруженных данных/управления ими.


Мост на Caldera Lake из God of War. Было довольно сложно управлять им на основании изменения освещения по ходу сюжета!

Прозрачные объекты и частицы


Наконец, есть прозрачные объекты и частицы, сильно отличающиеся от остальных категорий.

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

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

Совместимость с конвейером


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

Тестирование и запись глубин


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

«Вкусный» эффект глубины резкости и многие другие эффекты камеры требуют буфера глубин. Старый добрый туман глубины? Ему тоже нужен буфер глубин!


Мне нравится боке на этом скриншоте и работа, которую я проделал с художниками над Witcher 2, но обратите внимание на ошибки: пламя перед драконом выглядит резко, а плоскость перед фоном размыта. Это баг, потому что они должны иметь одинаковую глубину резкости (DoF), однако глубина, считываемая для эффекта боке — это глубина твёрдых объектов, а не частиц!

Кажется, что это неустранимое препятствие, ведь мы знаем, что объекты с альфа-смешением не записывают глубину и у них не может быть единого значения глубины… Но разработчики игр — мастера фокусов и имитаций. Если вам это нужно, можно создавать псевдоглубину вручную, сортировать и переупорядочивать некоторые объекты вручную, выполнять альфа-смешение глубины (это неправильно, но выглядит неплохо), настраивать глубину поля зрения, пока артефакты не перестанут мешать… Много ручной работы и головоломок вида «какой же компромисс наименее плох?»

Векторы движения


Близкий к глубинам компонент конвейера — запись векторов движения. Глубина необходима для многих вышеупомянутых эффектов, правильного перекрытия, отражений в экранном пространстве, тумана, и т.д., а векторы движения используются «только» для двух аспектов: размытия в движении и временного сглаживания (см. ниже).

Кажется, что размытие в движении (Motion blur) — это «просто эффект», однако незначительное его количество необходимо для снижения эффекта «стробинга» и ощущения дешевизны (см. motion blur полузакрытого затвора в фильмах с 24 fps).

(Немного хвастовства и ностальгии: размышления о motion blur заставляют меня ностальгировать — motion blur стал первой функцией, о которой я давал интервью прессе. Я до сих пор ощущаю гордость 23-летнего себя, живущего в Восточной Европе и ещё не закончившего колледж!)

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

В AC4 мы реализовали почти всё, за исключением океана и игнорировали в нём проблемы с TAA и motion blur.

Временное сглаживание


Временное сглаживание (Temporal antialiasing) — одно из величайших достижений движков рендеринга за последние несколько лет, но в то же время один из крупнейших источников проблем и артефактов. Я не буду обсуждать здесь его альтернативы и говорить, хорошая ли это идея, но оно останется с нами надолго — не только для сглаживания, но и для временного распределения сэмплов в методиках Монте-Карло, суперсэмплинге и стохастическом рендеринге, позволяя реализовывать в реальном времени более медленные вещи или картинку с повышенным качеством.

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


Мерцающий снег стал одним из самых востребованных функций художников God of War, но мне не удалось создать достаточно устойчивый эффект, потому что временное AA устраняло его полностью, считая временным мерцанием/алиасингом...

Отложенное затенение/освещение


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


Пример описания GBuffer — текстуры представляют различные свойства материалов и объектов. Позже к ним применяется освещение. Источник: learnopengl

Однако наличие «узкого места» в виде GBuffer и освещения, а также отсутствие доступа ко всем другим данным может сильно ограничивать.

Новая модель затенения? Новая таблица поиска? Новые данные, предварительно вычисляемые/запекаемые и сохраняемые в вершины или текстуры? Нужно уместить всё это в GBuffer!

Современные GPU с лёгкостью управляются с ветвлением и расхождением (при наличии частичной связности), но это может усложнить конвейер и приводить к созданию «эксклюзивных» функций.

Например, в God of War нельзя использовать подповерхностное рассеивание (subsurface scattering) одновременно с моделью зеркальных отражений ткани/светоотражения, потому что они используют одинаковые слоты в GBuffer. Сам GBuffer занимает очень много памяти и в игре было гораздо больше взаимоисключающих функций — я записал в свой блокнот многие возможные комбинации (если не ошибаюсь, там было 6 битов только для кодирования типа материала + его особенностей) и «торговался» об этих компромиссах с разными художниками, у каждого из которых были свои потребности.

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

Наличие/отсутствие переключения камер


В большинстве кинематографических вставок постоянно присутствует активное переключение камер, показывающее разных персонажей, разные перспективы на сцену или параллельно происходящие действия. Этот инструмент активно применяется во всех видах кино (возможно, за исключением «Догмы 95» или «Бёрдмена»), и если художник по кинематографии хочет его использовать, нужно обеспечить его поддержку.

Но что произойдёт, когда камера переключается со всей этой попиксельной историей временного накопления, необходимого для TAA/временного суперсэмплинга? Как насчёт потоковой передачи текстур, которая внезапно видит совершенно новый вид? Что насчёт амортизированного рендеринга, зависящего от вида, например, кэширования карт теней? Всё это решаемо, но потребуется много работы или могут возникнуть нежелательные задержки/рывки/слишком большие затраты на новый кадр. Мой коллега сказал, что это проблема и для физики или анимаций — часто при переключении камеры аниматоры изменяют позиции и мы видим, как физические объекты «оседают», например, на персонаже движется ожерелье. Это тоже разрушает погружение в игру и требует собственного набора «хаков».

Нехватка переключений камер (например, как в God of War) тоже сложна, особенно для освещения кинематографических сцен, хороших анимаций, переходов и т.д. Как бы то ни было, обе проблемы необходимо решать/учитывать. Стоит даже добавить в рендере флаг «камера была переключена».

Пирамида усечения


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


GIF из Horizon: Zero Dawn демонстрирует усечение по пирамиде видимости. Многие разработчики игр были немного удивлены тем, что игровые медиа и игроки «обнаружили» в играх усечение по пирамиде видимости (оно использовалось всегда).

Теперь всё становится сложнее! Зачем анимировать невидимые объекты? Не будем этого делать, чтобы сэкономить много времени ЦП.

Однако предметы, видимые в основной камере — это только часть истории, есть ещё и отражения с тенями… Не могу сосчитать, сколько раз я видел, что в тенях персонажи вне экрана находятся в «T-позе» (стандартная поза, когда персонаж не анимирован). Ещё одна новая проблема заключается в том, что трассировка лучей может «касаться» поз объектов!


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

Усечение по перекрытию


Усечение по перекрытию (Occlusion culling) — это следующий этап после усечения по пирамиде видимости. Нам ведь не нужно рендерить объекты за пределами камеры? Да. Но если перед камерой находится огромное здание, нам тоже не нужно рендерить весь город за ним!


Источник: “A Survey of Visibility for Walkthrough Applications”.

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

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

Уровень детализации


Любая методика, требующая некоего вычислительного бюджета на рендеринг и объёма памяти, должна правильно «масштабироваться» в зависимости от расстояния. Когда отрендеренный объект находится в 100 метрах от камеры и занимает всего несколько пикселей, он не должен занимать 100 МБ памяти, а его рендеринг/обновление не должно занимать и миллисекунды.

На сцене появляется уровень детализации (level of detail, LOD) — упрощённое представление объектов, мешей, текстур(последние реализуются автоматически в форме mip-текстурирования, но их всё равно нужно правильно и по отдельности потоково передавать), а также их вычисления.


Источник: Википедия.

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

Как должно масштабироваться глобальное освещение с расстоянием? А объёмные эффекты? Освещение? Частицы?

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

Загрузка, переключение и потоковая передача LOD


Уровень детализации нужно передавать потоково. Если объект вдалеке занимает на экране несколько пикселей, мы можем использовать десяток вершин и, может быть, вообще не применять текстуры — в целом это займёт несколько килобайт. Но потом камера начинает приближаться, и нам нужно загружать материал, текстуры, меши… Для всего этого нужно писать код систем и решений. Что произойдёт, когда камера просто телепортируется? Система потоковой передачи должна справляться с этим.

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

Открытый мир


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

Когда я присоединился к команде разработчиков AC4 в Ubisoft и увидел все технологии «открытого мира» для потоковой передачи, рендеринга на дальних расстояниях и тому подобное, или элементы теней на дальнем расстоянии в Far Cry, я был поражён, насколько специализированный (и продуманный) труд необходим для выпуска игр на консолях.

Бюджет


Наконец, о бюджете… Ниже я буду говорить о бюджете «продакшена», но, надеюсь, это понятно и так. Если новая методика требует памяти или тактов CPU/GPU, их необходимо выделить.

Часто встречается заблуждение о том, что если нечто укладывается в 30 мс — это «методика реального времени». Она может ею быть, но если рендерится только эта методика и только в игре на 30 fps.

В VR иногда приходится иметь дело с бюджетами меньше 10 мс на рендеринг камер для двух глаз для целого кадра! Это означает, что методика освещения, требующая 1 мс, уже слишком затратна!

Аналогично с ОЗУ — все затраты необходимо оценивать в целом и обычно игры уже и так используют всю свободную память. То есть ради любого нового компонента приходится жертвовать чем-то ещё. Стоит ли функция X снижения бюджета на потоковую передачу текстур и риска багов потоковой передачи текстур?

Ещё один «невидимый» бюджет — это пространство на диске. О нём говорят не очень часто, но при современных конвейерах видеоигр мы уже едва можем уместить игру на диски Blu Ray (в God of War это было большей проблемой, чем размер памяти или производительность!).

Можно вспомнить и о патчах, занимающих по 30 ГБ — это смехотворно и обычно является признаком того, что в системе упаковки ресурсов недостаточно хорошо учли патчинг. Как бы то ни было, новое решение с предварительно вычисляемым глобальным освещением, занимающее «всего» по 50 МБ на фрагмент уровня, для целой 30-часовой игры «внезапно» масштабируется до 15 ГБ на диске; скорее всего, это будет неприемлемо!

Процесс


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

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

Художественный контроль


Легко впасть в восторг от потрясающих результатов процедурного рендеринга, фотограмметрии или в целом захвата и повторного рендеринга 3D-контента — результаты выглядят великолепно и, казалось бы, обещают устранить огромную долю затрат на продакшен.

Однако художники должны иметь полный контроль над процессом создания контента и участвовать в нём.

Захват модели стула при помощи neural implicit representation выполнить легко, но сможете ли вы изменить цвет сиденья, удлинить некоторые части стула или сделать краску потёртой? А когда арт-директор решит, что нужен стул в стиле «барокко», вы будете искать его?

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

В Assassin’s Creed 4 один из моих коллег работал над процедурным рендерингом неба (в сотрудничестве с другими командами, а также, если не ошибаюсь, с вычислениями какого-то промежуточного ПО). Результат выглядел потрясающе, но арт-директор (который также был профессиональным художником) не мог получить именно тот вид, который ему нужен, поэтому мы продолжили работу с олдскульными, статичными (но красивыми!) скайбоксами.


Разработка процедурной системы рендеринга, способной создавать точно такое же небо, как на этом художественном концепте Assassin’s Creed 4, будет сложной задачей!

Инструменты


Любой творческий/художественный инструмент или методика должны иметь инструментарий для управления им, размещения на уровнях и обеспечения интерактивности. «Инструментами» может быть что угодно — от редактирования файлов CSV до специальных полнофункциональных WYSIWYG-редакторов.

Даже простейшие новые методики требуют каких-нибудь инструментов/элементов управления (хотя бы простейшего «вкл./выкл.»), а их необходимо написать.

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

Кроме того, если вы предлагаете нечто для замены представления мешей, вам необходимо подумать о том, что это внезапно влияет на всю экосистему, к которой привыкли художники — от создания контента в Z Brush, 3D Studio Max и Maya, инструментов анимирования и экспортёров наподобие Motion Builder или специализированного ПО для захвата движения, до всех плагинов движка, импортёров и экспортёров. Это схоже с переделкой десятков лет работы всей отрасли. Выполнимо? Вполне. Разумно? А это уже зависит от вашего бюджета и от преимуществ, которые могут дать такие изменения.

У меня есть теория о популярности движков наподобие Unity и Unreal Engine — она появилась не благодаря новым функциям (хоть их и много!), а из-за их зрелости и стабильного, выразительного и хорошо известного инструментария (см. ниже).

Обучение и опыт


Некоторые художники видеоигр работают уже пару десятков лет и наработали много знаний, «ноу-хау» и опыта. Если вы предложите нечто, не связанное с их опытом, то он окажется ненужным. Чтобы был стимул отвыкать от старых привычек, выгода должна оказаться огромной или стать стандартом всей отрасли (для популяризации Physically Based Rendering потребовалось несколько лет).

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

Поддержка других людей


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

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

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

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

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


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

Всё, что не тестировалось на практике. должно считаться сломанным или неработающим.

Отладчики и профилировщики


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

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

Разрабатывая новую методику, пытайтесь думать: «Если по какой-то причине что-то пойдёт не так, как я или кто-то менее технически опытный сможет понять это и устранить проблему? Какие инструменты для этого нужны?»

Пограничные случаи


Ох уж эти пограничные случаи. Вещи, которые должны быть «редкими» или даже ничтожными, но гарантированно происходящие при реальном крупномасштабном производстве. Меши с нарушенной герметичностью или сломанной топологией? Гарантированы. Треугольники с нулевой площадью? Гарантированы. Вывернутые нормали? Гарантированы. Материалы с нулевой шероховатостью? Безусловно. Чёрное или полностью белое albedo? Да. Единственный уровень, где стены должны быть такими тонкими, что просачивается глобальное освещение? Ага.

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

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

Практик всегда должен думать: «Как это можно сломать? Как можно устроить здесь деление на ноль? Что произойдёт, если x/y/z?», и в целом готовиться потратить большую часть времени на обработку и обеспечение надёжности этих пограничных случаев.

Бюджет продакшена


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

История о разработке Witcher 2: я реализовал спрайтовое боке в выходные примерно за две недели до выпуска gold master. Я был в полном восторге от него, и мой восторг разделяли художники по окружениям и кинематографическим вставкам. Мы были полны решимости внедрить его и потратили следующие две недели на упорную переработку параметров глубины резкости во всех созданных кинематографических вставках (очевидно, что модифицировать нужно было только некоторые). Я рад что тогда мы были очень неопытными и не знали, что так делать не нужно — эффект был очень крутым, я не могу представить, что такое могло бы случиться в моих последующих, более опытных компаниях.

Подведём итог


Подытожим: в случае графических алгоритмов путь от исследований к продукту может быть очень непростым!

Реализовать технологическое демо легко, а со следующими этапами не всё так очевидно:

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

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

Как я несколько раз говорил, я не хочу демотивировать никого от нарушения этих инструкций и переступания «практических знаний».

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

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

P.S. Если вам любопытны старые «военные истории» о рендеринге растительности в Witcher 2, то прочитайте следующий раздел.

Дополнение: пример использования — растительность


Я начал статью с шутки о том, как рендеринг растительности ломает всё остальное, но давайте более серьёзно проанализируем, сколько труда и обсуждений понадобилось для рендеринга растительности в Witcher 2.

Примерно в то время, когда я пришёл в CD Projekt Red на этапе препродакшена The Witcher 2, в игре не было «системы растительности» или специального решения для рендеринга листвы. Художники размещали растительность как обычные меши — один стебель травы за другим. Каждый меш был компонентом и объектом внутри объекта-сущности. Обычно он был очень тяжёлым и использовал «обобщённые» структуры данных. Одинаковые структуры использовались для камней, растительности и персонажа игрока. В течение продакшена нужно было изменить очень многое!


Импортёр и ветер Speedtree

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

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

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

Но это выглядело отлично и я признал, что был прав, когда мечтал стать программистом графики — это занятие даёт такую самоотдачу!

Просвечивание и нормали

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

Исправление импортёров, редактора мешей и отладка проблем с художниками, их (и моё!) обучение потребовали довольно много времени.

Выполнять альфа-тест или отрисовывать мелкие треугольники?

Когда мы стали доходить до более критичных для производительности этапов, растительность всё больше становилась проблемой для производительности. Альфа-тестирование затратно и приводит к перерисовке, а отрисовка мелких треугольников вызывает так называемую перерисовку четырёхугольников (quad overdraw). Оптимизация перерисовки растительности была бесконечной историей — помню, что когда мы начали профилировать некоторые уровни, на них присутствовал один ресурс, рендеринг которого занимал 8 мс даже на мощных PC. Мы провели множество итераций и экспериментов, чтобы заставить его работать быстро. В конечном итоге решение оказалось гибридным — множество отдельных стеблей травы, но листья были упорядочены как «карты листьев» с альфа-тестированием.

Слои мешей уровней

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

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

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

Автоматическая система билбордов

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

Хотя её реализовывал не я, позже я активно её совершенствовал и отлаживал (реализовавший систему программист был самым опытным человеком в команде, он был завален работой и обычно работал только над сырой реализацией, а затем передавал её менее опытным людям, и это была отличная возможность для обучения и менторства). Я работал над такими вещами, как области с альфа-тестированием для правильного mip-текстурирования или оптимизации структуры GBuffer (для избежания дополнительной упаковки/распаковки GBuffer).

Редактор растительности

Следующая работа над растительностью тоже была связана с инструментарием!

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

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

Усечение и инстансинг растительности

Внося вручную множество оптимизаций, мы приближались к дате выпуска Witcher 2 на PC и начинали работать над версией для X360. Новый программист, который теперь стал моим хорошим другом, исследовал производительность, особенно усечение по перекрытию на 360 (олдскульный «хипстерский» алгоритм, о котором вы, наверно, никогда не слышали — beam tree). Оказалось, что построение в каждом кадре рекурсивного BSP из сотен перекрывающих объектов и обработка тысяч мешей вместе с ним — не самая хорошая идея, особенно на процессоре PowerPC консоли X360.

Мой коллега потратил довольно много времени для того, чтобы превратить систему растительности в настоящую «систему» (а не просто контейнер для мешей) с иерархическим представлением, улучшенным усечением и аппаратным инстансингом (у него, в отличие от остальных членов команды, был опыт работы с ними на X360). На это тоже потребовалось много недель, и это была критически важная работа для выпуска игры на Xbox.

После Witcher 2 — рельеф и процедурная растительность

Но на этом история растительности в играх Witcher не заканчивается.

В Witcher 3 (учтите, что я мало работал над ней, меня перевели в команду Cyberpunk 2077) мои коллеги решили интегрировать системы рельефа и растительности с потоковой передачей (что необходимо для игры с открытым миром!), а наш потрясающий технический художник кодировал процедурную генерацию растительности на основе физических процессов (влияние высоты, количества солнца, близости воды) и игры «Жизнь», которые применялись для «засаживания» мира реалистично выглядящими растениями и лесами.

Я уверен, что происходило много всего другого. Но представьте, что вам приходится переделывать всю работу из-за какой-то новой технологии. Повторюсь, это реально, но стоит ли оно того?
Источник: https://habr.com/ru/post/574178/


Интересные статьи

Интересные статьи

Когда в марте 2020 года вирус поразил Европу, больницы погрузились в новый для себя кризис, не видя путей для выхода. Мы столкнулись с резкой нехваткой знаний. Врачи не понимали, как вести пациент...
Добрый день! Команда Fulcon приступила к реализации собственной платформы для индивидуального умного дома. Мы пришли к этому благодаря анализу рынка компаний и решений на территории Украи...
Технический директор Борис Горячев рассказывает, как «Медуза» работала над ним целый год и почему оно написано на Flutter 12 мая состоялся релиз новых мобильных приложений «Медузы» — почти через...
Тяжело искать ответы в бесконечном пространстве. Математика уровня старших классов может помочь вам сузить область поисков. Учитывая, что люди изучают свойства чисел тысячи лет, можно было б...
Язык программирования Ада родился в середине 1970-х, когда министерство обороны США и министерство обороны Британии решили заменить сотни специализированных языков программирования для встрое...