Skip to main content

Internals of Flatout 2 game rendering engine (ru)

· 5 min read

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

Сначала общие сведения. В кадре 70 - 120 тыс. полигонов. Все рисуется исключительно шейдерами 1.1 (что, в общем то, и не удивительно, учитывая то, что игра, также, вышла и на XBox). Дипов от 700 до 1100, что на удивление много.

Теперь процесс рендера поэтапно.

  1. Тени для машин. Каждый автомобильчик имеет свою текстуру с тенью (256х256, R5G6B5). Туда рисуются только кузов и колеса. Очень похоже, что это самый малоденальный LOD. Шейдер элементарный. Получается такая картинка:

Car Shadow

  1. Небо. Сделано просто и оригинально: само небо - полукуб. Легко представить себе, как кубик разрезанный горизонтально пополам. Затем на уровне горизонта кольцо, с панорамной текстурой гор, домов, леса и т.д. Удобненько, потому что, тип неба (вечернее, дневное) можно менять отдельно. Хотя, можно было все тоже сделать одним кубиком и одной кубемапой. Вышло бы меньше draw calls.

  2. Земля. Вот тут просто супер. Для каждой трассы есть одна (2048х2048) текстура земли, в которой рисунок уровня с видом сверху. Все статические тени от холмов, мостов, и других объектов уже на ней. При рендеринге используется эта текстура, и различные текстуры деталей (листики там, камешки). При их сложении получается очень симпатично. Модель освещения тоже очень простая и очень эффективная. Для всех статических объектов (не только земли) освещение предрасчитано, и хранится как диффузный цвет в вертексах меша. В реалтайме ничего не считается. В результате получаем очень короткие шейдеры, которые очень быстро исполняются. На этом этапе есть хорошая сортировка как по материалам, так и по текстурам.

Пример текстуры всей локации:

Map

Пример текстуры деталей:

Map details

  1. Кактусы, деревья. Есть статик батчингом. Я так и не понял, почему он то используется, то нет. Есть подозрение, что нефизические объекты за пределами трека рисуются кучами, а все остальные нет. На счет сортировки по материалам, то тоже не понятно. Хоть все рисуется одними и теми же шейдерами, но все равно каждые несколько draw calls обрамлены ID3DXEffect::Begin, BeginPass, EndPass, End. Освещение предрасчитано и тут. Шейдер такой же как и для земли. ps: 1+1 инструкций, vs: 6.

  2. Машинки, часть 1. Без колес и стекол. Используются самые сложные шейдеры в игре. Есть диффузное освещение с использованием кубемапы (A8, вверху светло, книзу темнеет), спекуляр от солнца. В ps, также, передаётся степень повреждения рисуемой детали, на основе которой выбирается текстура новой, либо повреждённой машины. В алфа канале текстуры авто содержится отражающая степень поверхности. Для отражений используют левую кубемапу. Она одинаковая для всех трасс. На ней деревца и горы. Никакой динамики.

Новая машинка:

Car new

Поврежденная машинка:

Car damaged

before_p6. Физические объекты. Используют диффузную кубемапу. Опять странная сортировка по материалам. Частичная какая-то. 7. Машинки, часть 2. Элементы двигателя, интеръер, колесные диски. Шейдер как для части 1. 8. Водитель. Хардварный скининг. 9. Тени под машинами. Строится меш, который стелится по земле. Сначала рисуется простое тёмное пятно. Затем используется динамическая текстура тени сделанная в п.1. Интересно то как они заблуривают тень. Шейдеры с комментариями:

vs_1_1
dcl_position v0
dcl_color v2
dcl_texcoord v3
m4x4 oPos, v0, c0
add oT0.xy, v3, c32 // c32 0.000 -0.004 0.000 0.000
add oT1.xy, v3, c33 // c33 -0.004 0.000 0.000 0.000
add oT2.xy, v3, c34 // c34 0.004 0.000 0.000 0.000
add oT3.xy, v3, c35 // c35 0.000 0.004 0.000 0.000

ps_1_1
tex t0
tex t1
tex t2
tex t3
mul r0, c7, t0 // В c7, как можно догадаться, 1/4. В каждом текстурном стейдже одна
mad r0, c7, t1, r0 // и та же текстура с тенью от машины
mad r0, c7, t2, r0
mad_sat r0, c7, t3, r0

Т.е. блурят семпля 4 раза одну и ту же текстуру, немного смещая её в стороны. Учитывая их малый размер (256), и то что используется хардварная фильтрация текстур, и , результат выходит просто отличный.

  1. Дальние + ближние кустики. То большие батчи (100-200 штук за раз), то по-одному. Простой шейдер.
  2. Машинки, часть 3. Шины, стекла, фары. Для последних двух, простые отражения, используя ту же кубемапу, что и для кузова автомобиля.
  3. Системы частиц. Дым, пыль, искры. Простейшие шейдеры.

Теперь всё нарисовано. Получилось вот так:

Before Post-Process

Далее пост-процесс.

  1. Считаем яркость картинки. Проще говоря, из цветной делаем черно-белую.
  2. Используя яркость пикселя (из ч.б. изображения) и специальной градиентной 1D текстуры делается color remap. Яркие пиксели получают один оттенок, темные другой. Вечером, например, освещённые участки немного красноватые. После ремапа, полученная картинка комбинируется с цветной, и получается вот такое:

Color remap

  1. Дайнсемплим изображение из п.14 в 256x256. Затем выделяем яркие места простым шейдером.
  2. Блурим пинг-понгом 4 раза, методом, аналогичным с методом блура тенюшек от машин. 4 tap, используются смещения в текстурных координатах. Получаем вот такую картинку:

Blur grayscale

  1. Объеденяем заблюреную текстуру, с основной картинкой. Получили glow еффект:

Blur grayscale

  1. Делаем 2 radial blur тем-же методом, только теперь текстурные координаты не спещаются, а с каждым разом увеличиваются. Получили изображение, которое используется при нитро-ускорении:

Blur grayscale

  1. UI. Тут все стандартно. Рисуем поэлементно. Сортировок никаких нет.

Это всё. Выводы, которые я сделал из этого маленького исследования:

  • Арт решает (я это и раньше знал, но убеждаюсь все больше). Во Flatout2 нет ни одной cutting-edge графической технологии. Новомодных технологий можно наклепать три корзины. Но если у вас нет хорошего арта, и грамотной (!) настройки этих всех эффектов - у вас все будет тормозить и при этом выглядеть отстойно.
  • Еще раз отметил для себя хороший принцип, который должен знать каждый разработчик игр: не надо считать каждый раз то, что можно посчитать один раз.
  • Сортировки по материалам и текстурам в движке скорее нет, чем есть.
  • Был немножко удивлен простоте, но не в ущерб качеству (хотя dip'ов можно было сделать и поменьше). Рендер прост и быстр.