Relic Entertainment secrets. Part 2 (ru)
Сорри за долгий перерыв, продолжаем копание в движках и играх. :) Как я и обещал, вторая подопытная игра от Relic - RTS Company Of Heroes (http://www.companyofheroesgame.com/). Продукт, несомненно, отличный, но меня в первую очередь интересовало, на сколько его рендер отличался от DoW 40K. Оказалось кардинально.
Из первой части мы хорошо помним, что в DoW всё сделано на FFP. Видно было, что СоН выглядит технологичнее, но вероятность того, что движки схожи, существовала очень и очень большая.
Вторая страшная тайна Relic заключается в том, что в CoH используется совершенно отличный от DoW рендер. Представьте себе автомобиль 1990 и 2006 годов выпуска, какой-нибудь известной марки (нет, ВАЗ не надо, они не развивались :)). Ауди, или БМВ подойдет. Старые машины тоже были качественно сделаны, и сейчас многие неплохо ездят, но то количество нововведений, технологий и опций, которые доступны сейчас, просто умопомрачительно. Так вот, качественный пр ыжок в движках от Relic примерно такой же.
Реверсил я на машине A64 X2 4200+, 2 гига памяти, GF 8800GTX. Все настройки в игре на максимуме.
FFP нету совсем. В основном используется shader model 3.0. Сразу хочется отметить очень навороченную систему освещения. Сделано очень грамотно и красиво. Но обо всем по порядку.
С самого начала создается, так называемая Point Light Map (в терминах самого Relic). Это обычная, изначально темная, текстура, в которую рисуются все яркие места на карте. На ночных уровнях это фонари прожекторов, например. Процесс простой: если смотреть на карту сверху, то в местах, где ярко, рисуем квадратики с текстурой блика. В результате получаем картинку, которая отдалённо напоминает спутниковую фотографию ночного города. Пример:
Затем идет 2 прохода в 2 различные карты теней. Первый для близких от камеры объектов (больше деталей), вторая для более дальних. Все видимые модели не захватываются. Если присмотреться, то видно как появляется тени на удалённой геометрии. Никаких хитромудрых техник (PSM, TSM) не реализовано. Естественно, используется NV shadow map hack. На ATI, думаю, будет использоваться ихние depth textures. Шейдеры простые, но есть один нюанс: всегда используется texkill для эмуляции альфатеста. Почему не использовали обычный альфатест?. Оказалось всё просто: в буфер цвета выводится глубина пикселя. По-видимому, программисты решили сэкономить на вариациях, чтобы сделать единственный шейдер для обычной R32F, а также NV и ATI depth textures. Это решение мне кажется спорным, так как, сделав ещё одно разбиение (без texkill’a и alphatest’a), получаем double speed z-only проход. Обе текстуры 2Кх2К.
Water reflection pass (если в кадре есть вода, естественно). Туда попадают все объекты, рисуемые шейдерами, которые используются в основном проходе (о нём далее). Единственная оптимизация, которая применяется, это куллинг дальних объектов. Если двигаться вдоль водной поверхности, то видно как появляются объекты в отражении.
Небо. Простая полусфера, рисуемая несложными шейдерами. Интересный момент №1: в игре используется Atmospheric Light Scattering. Я в этой сфере не силён, но после небольшого изучения появились подозрения, что используется упрощенная модель, которая была описана Naty Hoffman’ом и Arcot J Preetham’ом, на GDC 2002 (дополнительное описание есть в ATI SDK). А упрощенная потому, что в игре нет динамической смены дня и ночи, поэтому большинство коэффициентов можно предрасcчитать на CPU.
Пример текстуры неба:
Объекты на карте. Дома, юниты: всё тут. Шейдеры сложные. Вертексный шейдер для статичных моделек – 126 инструкций. Если скиненая – 150 (используется аппаратный скиннинг). Пиксельный общий - 65 инструкций. Что же такого там делается?
-
Интересный момент №2: в игре используется Spherical Harmonics Lighting. Подход очень сильно напоминает метод, который описал Tom Forsyth в заметках к своей презентации на GDC 2003 (http://home.comcast.net/~tom_forsyth/papers/SH_GDCE_TomF.zip. Блог тут: http://home.comcast.net/~tom_forsyth/blog.wiki.html. Смотреть пост за 17 января). В двух словах суть метода такова: 3-4 самых ярких источника мы обсчитываем как обычно (бамп, спекуляр, и т.д.). Все остальные переводим на CPU в SH коэффициенты и считаем освещение в вертексном шейдере. Грубо говоря, освещение с помощью SH можно считать диффузным освещением с использованием кубемапы. Только в реальном времени считать кубемапу не в пример накладнее 7 коэффициентов (именно столько используется в CoH). Использование всего семи первых членов полинома, вносит достаточно большую ошибку (обычно берут 9), но, по-видим ому, разработчики были удовлетворены результатами экспериментов.
-
Используется описанный выше Atmospheric Light Scattering. Визуально это выглядит как туманчик, которым покрываются дальние объекты.
-
Используются 2 карты теней. Искусственно мягкие не делали, ограничились бесплатным 2x2 PCF на NV железе. Как там дело на ATI, не знаю. Реализовать не сложно, но уже и так довольно тяжело тянуть эту шейдерную нагрузку.
-
Источники «средней важности» считаются в VS. Самые важные в PS c нормал-маппингом.
-
В пиксельном шейдере использутся:
Specular map
Gloss map
Ambient occlusion map
AO Detail map (в терминах Relic: Dirt map) Local ambient occlusion map. LAO Совершенно чудесная текстура. :) Мои долгие медитации над кодом шейдера не дали сколь-либо чётких результатов. Видно, что в разных каналах содержится разная информации о освещенности уровня с видом сверху. Происходит какая-то аттенюация базового цвета, с использованием этой текстуры. Каналы отдельно:
R:
G:
B:
Туман войны (считается на CPU):
Для разрушаемых объектов есть ещё и damage map. Накладывается по маске.
Вот так всё непросто. :)
Водичка. В принципе незамысловатая. Только отражения. Есть текстура с маской прибрежных зон, для имитации пены на волнах. Тот же light scattering. Направление ветра регулируется из приложения.
Земля. Уже 10 текстур занято, места для тайлов и маски совсем не остается. Выход простой – отмоделить и оттекстурить землю, как обычные объекты. Шейдеры используются абсолютно такие же, как для объектов, только вдобавок применяется еще и Point Light Map описанная в самом начале.
Партиклы. Сложный вертексный шейдер хитро считает освещение, зависящее от кучи входных параметров. Также он считает текстурные координаты, так как в Relic использовали хитрую систему запаковки текстур в атласы. Когда одн ой большой (2Кх2К) стало мало, а рисовать всё скопом очень хотелось, программисты вспомнили про кубемапы. Вот так на свет появилась огромная кубемапа (6х2Кх2К), в которой содержатся все изображения, необходимые для рендеринга эффектов. Вот примеры 2-х сторон:
На самом деле, очень необычное решение. Я такого ещё нигде не видел. В пиксельном шейдере вездесущий light scattering + странное смешивание семплов из кубемапы с nearest и point фильтрациями (2 семплера). Для чего они это делают? Я не разобрался. :)
В конце идут границы игровых зон и выделения юнитов. Тут тоже таится необычность. Это высокотесселированые модельки, которые считаются на CPU. Тяжело описать, лучше 1 раз увидеть:
Блум. Даунсемпл + color remap + фильтр Гаусса по горизонтали и вертикали.
UI. Вообще неплохой, но после всего остального сильно проигрывает в эффектности. Батчить можно было лучше. Текст по словам, а вот иконки на миникарте по одной.
Общие впечатления от рендера очень положительные. Технологично и чётко. При стандартном наклоне камеры (если Backspace нажать), на максимальной высоте 600-800 DIP’ов. Из них 100 DIP’ов UI. Если смотреть горизонтально, то 2000, но это не критично, так как так никто не играет. Как и в DoW, применена scissor rectangle оптимизация: область, закрываемая UI, отсекается. Это было не столь важно в DoW, но в CoH, с его супер шейдерами, важно. Естественно, всё это безобразие не без офигенного арта.
Мини выводы:
- В этот раз решил хороший баланс арта и технологии. :)
- Небольшие замечания по движку всё равно есть. Например, вместо Atmospheric Light Scattering’а, влияние которого видно только при горизонтальном положении камеры, можно было сделать обычный туман. Картинка пострадала бы не сильно, но мы бы получили прирост в скорости, за счет более лёгких шейдеров. Хотя в игре много cut-scenes… В общем это так, ни на что не претендующие мысли вслух. Отмечу также отсутствие double speed z, и UI.
- Движки DoW и СoH совсем непохожи друг на друга. Я не буду утверждать, что они были сделаны разными командами, но подозрение есть. Ещё одно доказательство «разности»: в CoH остались специальные маркеры, которые видны в PixWin. Это мне немного облегчило «разделку». А вообще, по таким деталям отмечаешь высокую культуру программирования.