[Перевод] Как происходит рендеринг кадра A Plague Tale: Innocence

qi7310oonn7yulhrcsdmw_rzfsy.png


Предисловие


Как и в других моих исследованиях, давайте начнём с введения. Сегодня мы рассмотрим последнюю игру французского разработчика Asobo Studio. Впервые я увидел видео этой игры в прошлом году, когда коллега поделился со мной 16-минутным геймплейным трейлером. Моё внимание привлекла механика «крысы против света», но мне не особо захотелось играть в эту игру. Однако после её выхода многие стали говорить, что она выглядит так, как будто сделана на движке Unreal, но это не так. Мне стало любопытно увидеть, как работает рендеринг и насколько вообще разработчики вдохновлялись Unreal. Ещё меня заинтересовал процесс рендеринга стаи крыс, потому то в игре она выглядела очень убедительно и к тому же является одним из ключевых элементов геймплея.

Когда я начал попытки выполнить захват игры, то подумал, что придётся сдаться, потому что ничего не срабатывало. Хотя игра использует DX11, который сейчас поддерживают практически все инструменты анализа, мне не удалось заставить работать ни один из них. Когда я пытался использовать RenderDoc, игра вылетала при запуске, и то же самое происходило с PIX. Я по-прежнему не знаю, почему так происходит, но к счастью, мне удалось выполнить несколько захватов с помощью NSight Graphics. Как обычно, я поднял все параметры до максимальных и начал искать подходящие для анализа кадры.

Разбивка кадра


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

Как обычно, давайте начнём с окончательного кадра:

e309bc01336450e755af378e9e07ebf9.png


Первым, что я заметил, был совершенно иной баланс в этой игре событий рендеринга по сравнению с тем, что я видел в других играх ранее. Здесь множество вызовов отрисовки, что нормально, но на удивление лишь немногие из них используются для постобработки. В других играх после рендеринга цветов для получения окончательного результата кадр проходит ещё много этапов, но в A Plague Tale: Innocence стек постобработки очень мал и оптимизирован всего до нескольких событий отрисовки/вычислений.

Игра начинает построение кадра с рендеринга GBuffer с шестью render targets. Интересно, что это все render targets имеют 32-битный беззнаковый целочисленный формат (за исключением одного) вместо цветов RGBA8 или других специфичных для таких данных форматов. Это представляло сложность, потому что мне приходилось декодировать каждый канал вручную с помощью функции Custom Shader из NSight. Я потратил много времени на выяснение того, какие значения закодированы в 32-битные targets, но не исключено, что всё равно что-то упустил.

1566ce6fe3f539b267b0c3ddc777de78.png


GBuffer 0

Первый target содержит в 24 битах некие значения шейдинга, а в 8 битах — какие-то другие значения для волос.

1b50a72056766e9cadd8b6a040bb98cd.png


GBuffer 1

Второй target выглядит как традиционный RGBA8-target с разными значениями управления материалом в каждом канале. Насколько я понимаю, красный канал — это metalness (не совсем понятно, почему ею помечены некоторые листья), зелёный канал выглядит как значение roughness, а синий канал — это маска главного персонажа. Ни в одном из сделанных мной захватов альфа-канал не использовался.

2cfa4072a62ca02ef7de7f9e9b5ed259.png


GBuffer 2

Третий target тоже выглядит как RGBA8 с albedo в каналах RGB, а альфа-канал в каждом сделанном мной захвате был полностью белым, так что я не совсем понимаю, что эти данные должны делать.

5f7c6d9e2b37a7ff18ec87c8389ca0a7.png


GBuffer3

Четвёртый target любопытен, потому что на всех моих захватах почти полностью чёрный. Значения выглядят как маска части растительности и всех волос/меха. Возможно, это как-то связано с просвечиванием (translucency).

18d91f69f594f6898c21805c9cd9164a.png


GBuffer 4

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

09e8d28dae99dc5af634a67eaa8e2214.png


Глубина из GBuffer 5

cd1c4b76c92413c4d6848e2e5ecdcdc8.png


Маска из GBuffer 5

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

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

94dbbb2dd8220f2494ef81785910ea39.png


Сумеречные лучи

После завершения карт теней можно вычислить освещение, но прежде в отдельный target рендерятся сумеречные лучи (god rays).

d58f88abfc4aee82a95a9404ff5e4f09.png


SSAO

На этапе вычисления освещения выполняется вычислительный шейдер для расчёта SSAO.

c3771766d0785d131f15633eb2c52010.png


Освещённая непрозрачная геометрия

Освещение добавляется из кубических карт и локальных источников освещения. Все эти разные источники освещения в сочетании с отрендеренными выше targets в результате формируют освещённое HDR-изображение.

1ec40b716cf2715f98d2c4f6a72bb83c.png


Элементы, отрисовываемые упреждающим рендерингом

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

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

cdd73cddb19f72c7f9e228ee3b65ceef.png


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

98bedbed24528e052b396786b027369d.png


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

Стоит упомянуть пару интересных вещей, касающихся рендеринга:

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


Крысы


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

8658750c7c37151679f4ca5c12f196e8.png


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

bf50bcb334685d1b8e332ec5d386ecca.png


LOD0

fcff8e9dd94ec9a4537ea781991be2c2.png


LOD1

35fd4b2f2f4f1a29ea10502b002dc3ce.png


LOD2

d92c13e16d465a0d900d8cd59d288578.png


LOD3

У крыс есть 4 уровня LOD. Интересно, что на третьем уровне хвост загнут к телу, а у четвёртого хвоста вовсе нет. Вероятно это означает, что анимации активны только для первых двух уровней. К сожалению, у NSight Graphics, похоже, не хватает инструментов, чтобы это проверить.

f544d562f3b2434ffb6f47e665db27fc.png


Без дублирования (instancing) крыс.

20c89a9c25d0c9039ee691b4e1f5500e.png


С дублированием.

zw2ft16y68vu84ugwmiblagsfsa.gif


В сцене, захват которой показан выше, отрендерено следующее количество крыс:

  • LOD0 — 200
  • LOD1 — 200
  • LOD2 — 1258
  • LOD3 — 3500 (с дублированием геометрии)


Это даёт нам понять, что существует жёсткое ограничение на количество крыс, которых можно отрендерить на первых двух LOD.

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

В заключение


Plague Tale: Innocence очень интересна с точки зрения рендеринга. Его результаты без сомнений меня впечатлили, они очень хорошо служат геймплею. Как и в случае с любым проприетарным движком, было бы здорово услышать более подробный анализ из уст самих разработчиков, особенно потому, что мне не удалось подтвердить некоторые из моих теорий. Надеюсь, моя статья когда-нибудь доберётся до кого-нибудь из Asobo Studio и они увидят, что у людей есть к этому интерес.

© Habrahabr.ru