Deus Ex Human Revolution — это компьютерная игра 2011 года, которая является более успешным продолжением оригинальной Deus Ex, чем Invisible War. Но этот пост не о качестве игры, а о демонстрации её технических принципов. Адриан Курреж провёл несколько часов за реверс-инжинирингом, пытаясь понять с помощью инструмента Renderdoc, как происходит обработка каждого из кадров Human Revolution, и изложил результаты в своём блоге.
Игра построена на основе модифицированного проприетарного движка Crystal от компании Crystal Dynamics. Human Revolution была одной из первых игр, которая использовала 11 версию DirectX. На момент выхода уровень графики был на отличном уровне, похождения Адама Дженсена по вентиляционным каналам неплохо смотрятся и сейчас. При этом игра была не слишком требовательной к аппаратной составляющей компьютера.
Для рассмотрения Адриан выбрал этот кадр. Так его видит игрок.
На первый взгляд может показаться, что Human Revolution использует технику рендеринга Forward+. Но популяризация этого метода случилась куда позже выхода игры, и «Человеческая революция» обходится схемой Light Pre-Pass.
Всё начинается с построения карты нормалей и карты глубины. Движок рендерит все объекты, пропуская прозрачные. В зависимости от сетки, каждый из треугольников либо будет представлен как плоская поверхность, либо будет использовать собственную карту нормалей. К примеру, здесь у статуи в виде руки есть своя карта нормалей.
Одновременно происходит рендеринг карты глубин. Всего для производства обеих компонент потребовалось 166 вызовов отрисовки.
Тени генерируются с помощью Parallel-Split Shadow Maps. Каждая из теней рендерится единожды для каждого из источников света, которые могут при взаимодействии с объектами создавать тень. В данной сцене источников света два: один в офисе справа, другой на вершине скульптуры-руки. Каждая из карт теней представляет из себя квадрат 1024×1024 внутри текстуры 4096×3072.
Мелкие объекты пропускаются, возможно, часть невидимых для источника света не учитывается, поэтому этот проход требует всего 52 вызова отрисовки. Карты теней и карта глубин собираются для создания текстуры маски теней. Каждый тексель из карты глубин читается, и его видимость считается для каждого из источников света. Конечный результат выдаётся в 8-битной текстуре RGBA, которая работает как маска. Значение по умолчанию — белый цвет (1, 1, 1, 1), которое означает, что тексель ничем не затемнён. Если тексель попадает в тень какого-либо источника света, то байт, отвечающий за этот цвет, приравнивается нулю. Так можно обрабатывать 4 источника света.
Конечно, использовать байт для хранения только единицы и нуля слишком затратно, поэтому во время этого прохода также выполняется percentage close filtering (PCF), и в этих байтах хранится значение между 0 и 1, а не только крайние значения. Это нужно, чтобы края теней имели плавные переходы.
По буферу глубины создаётся карта преграждения окружающего света в экранном пространстве (Screen Space Ambient Occlusion, SSAO). Если видеоускоритель поддерживает DirectX 11, то шейдером создаётся блюр с ядром 19×19. На старых карточках это делается пиксельным шейдером.
Конечный результат с размытием
После генерации значение текстуры SSAO хранится в альфа-канале карты нормалей.
Каждый из точечных источников света обрабатывается по одному. Используется только карта нормалей с SSAO и буфер глубины. Эффект на пиксели зависит только от радиуса света и интенсивности. На этой стадии отражение света различными материалами пока неважно, карта освещения показывает, сколько и какого цвета потенциально отражается. Реальное отражение будет посчитано позже на основе характеристик материала. В конкретно этой сцене 45 точечных источников света.
Наконец происходит «реальный» рендеринг, в котором выводится сетка каждого объекта. Цвет каждого пикселя определяется картой нормалей и данными SSAO, масками теней и картами теней, картой освещения, текстурами объекта и свойствами материалов, иногда также используется карта для улучшения отражений. Сначала рендерятся непрозрачные объекты. При рендере используются данные буфера глубины, полученные при составлении карты нормалей.
Затем добавляются декали (таблички на стенах, следы от пуль), прозрачные объекты (стекла в окнах) и искусственные объёмные эффекты освещения.
Добавлены прозрачные объекты и декали
Эффекты освещения являются группой спрайтов, которые отрендерены в 3D. Это не просто плоские объекты, постоянно обращённые к камере, это двадцатигранники специального масштаба. Свечение обсчитывается полностью процедурно.
Рендер непрозрачных и прозрачных объектов был выполнен с помощью 253 вызовов отрисовки.
Для добавления эффекта засвечивания bloom нужно знать, какие области очень яркие. Human Revolution использует LDR, буфера HDR нет. При предыдущем проходе в альфа-канал передавались данные по интенсивности.
Теперь достаточно просто применить гауссово размытие с большим радиусом. Для эффективности изображение уменьшается в два раза, затем ещё в два раза, затем применяется размытие. Полученное добавляется поверх кадра без затемнений.
Важной составляющей является сглаживание, иначе изображение будет выглядеть плохо из-за «лесенок» границ. Human Revolution поддерживает множество методов: DLAA, MLAA, FXAA и так далее. В примере Адриана используется FXAA.
Всё почти готово. По соображениям стиля игра обладает золотистым оттенком, его, конечно, можно убрать с помощью стороннего мода. Но по умолчанию выполняется коррекция цвета.
Самым последним шагом является добавление пользовательского интерфейса. Это достигается за 317 вызовов отрисовки. Затем изображение выводится на экран.
Вот соотношение времени, которое требуется для выполнения каждого из шагов.
Но есть и другие интересные детали. В катсценах и диалогах используется эффект глубины резкости (Depth of Field, DoF), когда элементы не в фокусе размыты.
Изображение оригинальной сцены уменьшается в два раза
Готовый результат на экране пользователя. В зависимости от карты глубин пиксельный шейдер будет использовать либо части размытого изображения, либо оригинальное без размытия.
В Human Revolution неплохой эффект силуэта. Это подсветка интерактивных элементов игрового пространства, в конкретном случае золотой обводкой выделяется один объект, с которым игрок может взаимодействовать. В некоторых играх этот эффект реализован примитивно, к примеру, силуэт может отрисовываться уже после рендера всей сцены. Но в Human Revolution силуэт рисуется таким образом, что любой посторонний объект перед подсвечиваемым тоже обводится. В примере, который привёл Адриан, жёлтая линия описывает не только форму мусорного контейнера, но и проходит по фигуре полицейского.
Сцена, которую Адриан привёл для примера
Этот эффект достигается с помощью простого трюка. Информация о пикселях объекта, с которым можно взаимодействовать, хранится в альфа-канале карты освещения.
Это вся информация, которая нужна для отрисовки. До шага с эффектом bloom и после рендера сцены выполняется дополнительный проход. Пикселям подсвечиваемого объекта придаётся желтоватый оттенок, на них добавляется узор из треугольников, а границы силуэта обводятся с помощью некоторого подобия оператора Собеля.
По материалам блога Адриана Куррежа.
Автор: FakeFactFelis