?

Log in

[icon] Старкрафт 2: Секреты технологий - Роман Марченко
View:Свежие записи.
View:Архив.
View:Друзья.
View:Личная информация.

Security:
Subject:Старкрафт 2: Секреты технологий
Time:06:34 pm
Мой последний реверс датирован 4 июля 2008 года. Оправдываться не буду, что обещал чаще, а получилось вот так. Наконец кривые желания и возможности пересеклись, и вашему вниманию предлагается реверс инжиниринг рендера всем известной игры Старкрафт 2, от не менее известной компании Близзард.


Технологии шагнули далеко вперед. Уже можно играть в игры с поддержкой DX11. Есть игры которые изначально разрабатывались на DX10 и не поддерживают DX9 железо. Но, игры компании Близзард всегда славилась поддержкой широкого спектра аппаратуры (взять тот же ВоВ, который и на ФФП нормально бегает). Не исключением стала и SC2. Рендер стратегии разработан с использованием D3D версии 9 (для windows). DX10 не поддерживается. Я думаю после нескольких лет разработки было накладно переводить рендер на новый АПИ, поэтому имеем только DX9 (и Open GL для Mac).

Первое с чем я столкнулся после того как я сел за дело были проблемы с тулзами. СК2 использует хитрую систему запуска, через всякие лоадеры, так что под PerfHUD’ом запустить не удалось. Можно было бы поковыряться в исходниках лоадера (который форсит PerfHUD девайс), чтобы скипал несколько процессов, но из-за того, что стар молча выходил, при попытках тестовых запусков, я отложил вариант с PerfHUD’ом. Плюс некоторый функционал этого профайлера перестал работать на новой Win7 OS (не показывает перфоманс графики). Новый профайлер Nvidia Parallel Insight показал себя с лучшей стороны. Он быстрый, удобный и практически не глючный. Один недостаток – DX9 не поддерживается. Так что у меня оставался всего один вариант – MS PIX for Windows. За два года данную тулзу немного улучшили (особенно в плане производительности), так что пользовался я исключительно ей.

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

Вот как выглядит шейдер который я ожидал: http://www.everfall.com/paste/id.php?fgnlr6ffcx0y
Вот так выглядят шейдера от Близзард: http://www.everfall.com/paste/id.php?njt5do17c4bi
Что в первом, что во втором случае разобрать по асму что там происходит сложно, но в первом примере есть имена переменных, по которым можно сориентироваться. В общем, я понял, что так я далеко не уеду, и полез ковырять данные игры. Распаковал пару MPQшек, и нашел исходники шейдеров. Зачастую мне было сходу понятно, что делается в данном draw call. Но иногда я заходил в тупик. Помогало медитирование над входными текстурами, вертекс форматом и константами объявленными в шейдере. Если вижу, например, операцию вычитания двух регистров, один из которых константный со значением 1.5, я ищу по всем hlsl шейдерам текст -1.5 (или просто 1.5) и часто с первого раза попадаю в нужный. Если шейдер небольшой, то разбирал прямо по ассемблеру.

По рендеру СК2 есть замечательная дока: http://developer.amd.com/documentation/presentations/legacy/Chapter05-Filion-StarCraftII.pdf
В ней описаны многие аспекты рендера, и я ей активно пользовался. Рекомендую к ознакомлению.

Данный разбор касается только игровой части рендера. Рендер роликов будет отдельным постингом.

Хочется остановиться на системе материалов используемой в СК2. Применяется техника убершейдера, с плотной интеграцией в С++ код. Вот пример главной функции пиксельного шейдера и вычисления цвета материала: http://www.everfall.com/paste/id.php?bjpc19uks7he. Как видно, в исходном коде имеются множественные проверки булевых констант, которые определяют разветвления комбинаций опций шейдинга. Система пиксельной обработки построена на т.н. слоях (layers) каждый из которых состоит из одной текстуры (вернее самплера) и параметров-свойств этого слоя. Вот как оно реализовано: http://www.everfall.com/paste/id.php?5ekjmezzcllx. В шейдере, перед телом, идет декларация нескольких слоев:

DECLARE_LAYER(Diffuse);
DECLARE_LAYER(Decal);
DECLARE_LAYER(Specular);
DECLARE_LAYER(Emissive);
DECLARE_LAYER(Emissive2);
DECLARE_LAYER(Envio);
DECLARE_LAYER(EnvioMask);
DECLARE_LAYER(AlphaMask);
DECLARE_LAYER(AlphaMask2);
DECLARE_LAYER(Normal);
DECLARE_LAYER(Heightmap);
DECLARE_LAYER(Lightmap);
DECLARE_LAYER(AmbientOcclusion);

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

Рендер сцены начинается с рендера карты тени (shadowmap) размером 2048х2048 пикселов. Трик с нулевым рендер таргетом не используется. Соответствующий буфер цвета используется для рендера полупрозрачных объектов с позиции источника света для создания цветных теней. Также для полупрозрачный объектов используется отдельная карта теней с тем же размером. Искажающие перспективу и каскадные техники не используются по понятным причинам. Камера никогда не смотрит вдаль, и в нормальном игровом процессе расположена под углом 60-70 градусов к поверхности земли. В данной ситуации ни каскадные тени ни перспективное искажение возле положения камеры не дадут сколь видимого эффекта.

Пример шадоумапы:


Пример буфера цвета для цветных теней:


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

Оригинальная текстура шума:
Image Hosted by ImageShack.us

Полученная карта высот:
Image Hosted by ImageShack.us

Карта нормалей полученная из карты высот:
Image Hosted by ImageShack.us

Заметьте, что оригинальная карта высот сделана тайлящейся по всем направлениям. Т.о. мы имеем тайлящуюся карту нормалей.

Дальше начинается основной рендеринг. Устанавливается 3 рендертаргета формата ARGB16F в первые 3 MRT слота. Первый RT содержит освещенную глобальными источниками света картинку. Второй нормали в пространстве камеры. Третий только диффузный цвет.




Система освещения в СК2 комбинированная. Глобальные источники, как то солнце и те что занимают много места на экране, применяются сразу в первом проходе (т.е. forward shading). Множество мелких источников применяются после, с помощью аддитивного блендинга (т.е. deferred shading). Deferred источники используют стенсил для минимизации обрабатываемых пикселей. Стенсил буфер очищается, и перед отрисовкой deferred источника, рендерится его футпринт (т.е. шар для точечных, конус для спот источников). Сначала помечаем всю поверхность источника света, а затем исключаем из стенсил маски те участки, где прошел тест глубины для бэк-фейс полигонов. Т.о. получается маска пикселов которые буду затронуты данным источником освещения. Forward источников может быть не много, упираемся в шейдерные константы:
#define MAX_DIRECTIONAL_LIGHTS 3
#define MAX_POINT_LIGHTS 5
#define MAX_SPOTLIGHTS 5

Deferred источников может быть бесконечное множество.

Перед рендером геометрии с шейдингом идет depth prepass, в котором заполняется буфер глубины патчами земли. Почему только земля? Потому что, неровности земли часто скрывают элементы ландшафта, а остальные объекты небольшие. Таким образом, экономим еще немного производительности на пиксельной обработке.

Шейдеры главного прохода очень большие. Наложение теней, полупрозрачных теней, параллакс маппинга (в коде шейдера реализованы методы relief mapping и parallax occlusion mapping. Хорошо видно после взрыва протосовского здания – котлован взрыва), тумана войны, наложение крипа, того же направленного освещения. Блендинг кучи текстур. Сплаттинг для террейна часто не помещается по инструкциям в один большой шейдер. Слоя земли смешиваются в отдельном RT, а затем этот RT используется как диффузная текстура в рендере патча земли. Все движения выполняются посредством применения скелетной анимации.

Обычные тени размываются посредством percentage closer filtering’а. Для рандомизации семплов используется текстура с 2D rotation векторами:
L канал:
Image Hosted by ImageShack.us

А канал:
Image Hosted by ImageShack.us

Туман войны считается на CPU и хрантися в небольшой текстурке:
Image Hosted by ImageShack.us
Эта текстура используется для всех эффектов в игре.

Полупрозрачные тени детально описаны в документе от разработчиков. Опишу вкратце. У нас есть карта тени основная, карта тени в которой только глубины полупрозрачных объектов, и цветная текстура отрисованная с позиции источника света с полупрозрачными объектами. Когда мы делаем тест на то в тени ли пиксель или нет, при отрицательном результате теста (не в тени) мы тестируем его еще раз, но уже используя shadowmap с глубинами полупрозрачных объектов. Если тест положительный (в тени от полупрозрачного объекта, но не в тени от непрозрачного), то берем цвет из цветной текстуры и окрашиваем тень в этот цвет. Эффект хорошо рядом с кристаллами и под прозрачными юнитами.


Если на уровне есть вода, то дополнительно рендерится refraction текстура, а затем используется при рендеринге поверхности воды. Полупрозрачные объекты и системы частиц рисуются после всех непрозрачный, что логично.

После рендера всей геометрии идет проход deferred источников света, о которых я писал выше.

Далее идет отрисовка рамок UI, маркеров юнитов и портрета текущего выбранного юнита. К портрету юнита применятся depth of field.


Затем идет брайтпасс, и блур. Получаем блум эффект. После этого комбинируем все с тонемаппингом.
Далее отрисовка миникарты со всем маркерами юнитов (одним вызовом), туманом войны и крипом.


Поэлементно дорисовывается весь интерфейс. Текст рендерится пачками – применяется рендер скейлформа, который пакует все нужные буквы в кадре в текстуру. Получается эффективно.
Image Hosted by ImageShack.us

Результат:


Формат вершин моделей сделан хорошо, зачастую 32 байта. Данные упакованы хорошо. Количество DIPов тоже неплохое. В средней сцене – 800-1200. Лимит зерглингов на экране с основной базой показал 1800-1900 DIPов. Все карты нормалей хранятся в DXT5 формате со сжатием и распределением по каналам для большего качества. Реверс инжиниринг проходил на компьютере Core 2 Duo, GF460GTX, все настройки ультра. Качество текстур – высокое (при попытка захвата кадра пиксом, на настройках текстур ультра, старкрафт падал с out of memory).
comments: Оставить комментарий Previous Entry Поделиться Next Entry

brukhammer
Link:(Link)
Time:2010-11-02 08:18 pm
Очень круто!! нет слов.
(Ответить) (Thread)

daradiboga
Link:(Link)
Time:2010-11-02 10:00 pm
Спасибо!
Аж думал больше не будет!
(Ответить) (Thread)


balmerdx
Link:(Link)
Time:2010-11-03 09:20 pm
Спасибо, очень интересно.
(Ответить) (Thread)

(Anonymous)
Subject:Юниты
Link:(Link)
Time:2010-11-03 11:19 pm
А юниты у них как рисуются? Инстансинг есть? Сколько костей на модель? Как освещается?
(Ответить) (Thread)


__vortex__
Subject:Re: Юниты
Link:(Link)
Time:2010-11-04 11:35 am
Юниты рисуются тем же убершейдером что и все остальное. Принципы освещения теже. Инстансинг для моделей не используется, так как все вскелетно анимировано. В шейдерные константы помещается всего один скелет (64 3х4 матриц), а vertex texture fetch не поддерживается на всей нужной аппаратуре. На каждый вертекс влияет максимум 4 кости.
(Ответить) (Parent) (Thread)


cofffe
Link:(Link)
Time:2010-11-04 10:00 am
очень похожий тек по шейдерам в был у нас. (с сетапом конкретного шейдера из одного убер с помощью констант)
(Ответить) (Thread)


cofffe
Link:(Link)
Time:2010-11-04 10:01 am
только мы пользовали константы препроцессора, что несколько усложняло чтение кода шейдера.
(Ответить) (Thread)


__vortex__
Link:(Link)
Time:2010-11-04 11:36 am
Убершейдер давно известный и широко применяемый подход. Просто у каждой системы есть ньюансы реализации (что естественно).
(Ответить) (Parent) (Thread)


igroman14
Link:(Link)
Time:2010-11-04 06:44 pm
Спасибо, ждём пост про рендер роликов!;)
(Ответить) (Thread)


yak32
Link:(Link)
Time:2010-11-09 03:38 pm
интересно, как они ландшафт сделали. Те же клипмапсы? Или как в первом сталкере одинаковыми кусками?
(Ответить) (Thread)


yak32
Link:(Link)
Time:2010-11-09 04:17 pm
хотя смысла в клипмапсах при такой камере нет
(Ответить) (Parent) (Thread)


__vortex__
Link:(Link)
Time:2010-11-09 08:40 pm
Да, смысла нет. Рисуется квадратными патчами, каждый где-то на девятую часть экрана.
(Ответить) (Parent) (Thread)


sindicollo
Link:(Link)
Time:2010-11-12 04:40 pm
кроме вышеперечисленного софта есть еще Intel GPA.
(Ответить) (Thread)


__vortex__
Link:(Link)
Time:2010-11-12 11:55 pm
Он работает на любой аппаратуре? Я не помню просто. Но я его как-то глядел. Давно правда. Есть еще AMD GPU PerfStudio. Но работает только на Ати. В статье я упомянул мой рабочий набор тулзов, который я всегда использовал во всех реверсах.
(Ответить) (Parent) (Thread)


karpolan
Link:(Link)
Time:2010-11-13 06:30 pm
Дякую. Информативненько :)
(Ответить) (Thread)

(Anonymous)
Link:(Link)
Time:2010-11-14 12:24 am
Очень интересно написано! Спасибо!
(Ответить) (Thread)

(Anonymous)
Link:(Link)
Time:2012-07-08 10:40 pm
а что за трик с нулевым рендер таргетом? =)
(Ответить) (Thread)


__vortex__
Link:(Link)
Time:2012-07-09 09:12 am
Когда мы рендерим в шадоумапу, используя поддержку depth текстур в D3D9, то нам нужна только эта depth текстура. Но D3D требует, чтобы при рендере чего либо рендер таргет всегда присутствовал в нулевом слоте, и размеры рендер таргета и depth-stencil surface совпадали. Т.е. по сути нам надо создавать еще и рендер таргет размером с шадоумапу и стаивть его в RT слот во время рендера. Получается он просто сжирает память и никакой полезной нагрузки не несет. Вендоры (АТИ, Нвидия) сделали поддержку специального формата рендер таргета, который называется NULL Render Target. С точки зрения D3D все корректно. Мы его создаем, размеры нужные, но драйвер не выделяет для него никакой памяти. И мы его используем при ренедеринге. Получается и D3D рантайм спокоен, и мы пямять не теряем.
(Ответить) (Parent) (Thread)

[icon] Старкрафт 2: Секреты технологий - Роман Марченко
View:Свежие записи.
View:Архив.
View:Друзья.
View:Личная информация.