В недавно выпущенной игре Marvel's Spider-Man во многих зданиях за окнами есть интерьеры. Они выглядят замечательно, но похоже, что их реализовали с помощью хитрого рендеринга — геометрии интерьеров на самом деле не существует и она сгенерирована шейдером. Я не видел никаких официальных заявлений Insomniac о том, как они это сделали, но исходя из того, как выглядит эффект, здесь с большой вероятностью реализована техника interior mapping, которую я придумал в 2007 году в процессе работы над диссертацией. Раньше я не писал о ней в блоге, поэтому сейчас подходящий момент для объяснения любопытного небольшого шейдера, который я придумал.
Давайте начнём с просмотра геймплея Marvel's Spider-Man. Игра выглядит просто потрясающе. Сайт Kotaku записал отдельный ролик, посвящённый окнам:
Как можно заметить примерно на 40 секунде видео, в самом деле комнаты не являются частью геометрии: там где очевидно должно быть окно, находится дверь. Кроме того, глядя в одну комнату с разных углов здания, мы видим разный интерьер. В некоторых случаях за углом здания даже находится стена. Всё это даёт нам понять, что комнаты имитируются. Тем не менее, с точки зрения перспективы они отображаются правильно и обладают реальной глубиной. Думаю, недостатки таких комнат при игре не очень важны, потому что игроки обычно не изучают комнаты столь пристально: интерьеры — это просто фон, а не предмет внимательного исследования. Я считаю, что такой способ создания комнат добавляет городу глубины и жизни, не тратя при этом слишком много ресурсов.
Для экономии ресурсов здания в играх часто не имеют интерьеров, как это можно увидеть на скриншоте из GTA V
Прежде всего хочу объяснить, что мой пост не является жалобой: я в восторге от того, что мою технику использовали в такой масштабной игре и ни в коем случае не обвиняю Insomniac в краже. Как я говорил в первой публикации о interior mapping, для меня будет честью, если кто-то использует эту технику. Если Insomniac действительно воспользовалась в своей технике моей идеей, то я считаю, что это замечательно. Если не воспользовалась, то, похоже, она придумала нечто до странности похожее. Тогда мне было бы интересно, как это было сделано.
Так как же работает interior mapping? Идея заключается в том, что само здание не содержит никакой дополнительной геометрии. Интерьеры существуют только в шейдере. Этот шейдер выполняет raycasting со стенами, потолками и полами, чтобы вычислить, что игрок должен видеть в интерьере.
Слева направо: только окна с отражениями, окна с Interior Mapping, каркасная модель — Interior Mapping не добавляет никаких полигонов.
Используемый для raycast луч — это просто луч из камеры до пикселя. Пиксель, который мы рендерим, находится на наружной части здания, поэтому мы используем только часть луча за пикселем, потому что это именно та часть, которая на самом деле находится внутри здания.
Raycasting (испускание лучей) может показаться сложной и затратной операцией, но в этом конкретном случае она на самом деле очень проста и быстра. Хитрость в том, что можно добавить простое ограничение: при interior mapping потолки и полы находятся на неизменном расстоянии. Зная это, мы можем запросто вычислить, в какой комнате находимся, и где в этой комнате расположены пол и потолок. Сами по себе потолки и полы являются бесконечными геометрическими плоскостями. Вычисление пересечения между бесконечной плоскостью и лучём занимает всего несколько шагов и тратит мало ресурсов.
У комнаты есть 6 плоскостей: потолок, пол и 4 стены. Однако нам нужно учитывать только три из них, потому что мы знаем, в каком направлении смотрим. Например, если мы смотрим вверх, то нам не нужно проверять пол внизу, потому что мы будем видеть потолок наверху. Аналогичным образом вместо 4 стен нам нужно учитывать только две в том направлении, в котором мы смотрим.
Чтобы определить, что же мы видим, нужно вычислить пересечение луча с каждой из этих трёх плоскостей. Ближайшее к камере пересечение с лучом сообщает нам, какую из плоскостей мы видим в этом пикселе. Затем мы используем точку пересечения как координату текстуры, чтобы найти цвет пикселя. Например, если луч пересекается в позиции (x,y,z) с потолком, то мы используем в качестве координат текстур (x,y) и игнорируем z.
Здесь я добавил хорошую оптимизацию: часть вычислений пересечений для каждой из трёх плоскостей можно выполнять одновременно. Используемые шейдеры работали с float4 с такой же скоростью, как и с float, поэтому благодаря умной упаковке переменных можно было выполнять пересечения всех трёх лучей с плоскостями одновременно. Это сэкономило мне немного ресурсов и помогло достичь высокой частоты кадров при interior mapping даже в 2007 году. Мне говорили, что современные видеокарты с float работают быстрее, чем с float4; значит, на нынешнем железе эта оптимизация больше не работает.
Interior Mapping без текстур окон показывает, что комнаты рендерятся с правильной перспективой и с текстурами, хотя дополнительная геометрия и не требуется.
Подробнее о работе interior mapping можно узнать в моей статье. Эта статья была опубликована на Computer Graphics International Conference в 2008. Наличие настоящей рецензированной публикации — это моя первая (и единственная) заявка на гордое звание учёного. В этой статье также описываются дополнительные эксперименты по добавлению деталей, например, изменение расстояния между стенами для комнат неодинакового размера и случайный выбор текстур из текстурного атласа для большей вариативности комнат. Также в ней подробно описываются две вариации, показанные на изображениях ниже.
Освещение в комнатах можно динамически включать и отключать для имитации смены дня и ночи. Это выполняется с помощью текстуры шума, из которой мы выполняем чтение, используя в качестве координат текстуры индекс комнаты.
Так как мы всего лишь испускаем лучи в плоскости, все комнаты являются простыми квадратами с текстурами. Вся мебель в комнате будет находиться на текстуре, а значит оставаться плоской. В Spiderman это заметно в случае приближения камеры: столы в комнатах на самом деле являются плоскими текстурами на стенах. Как видно на показанном ниже изображении, можно дополнить нашу технику с помощью одной или нескольких дополнительных слоёв текстур на комнату, но это связано с дополнительными затратами производительности.
Выполняя трассировку лучей (raytracing) ещё одной плоскости, параллельной внешней поверхности здания, можно добавить в комнату мебель и людей. Тем не менее, они всё равно останутся плоскими.
После публикации этого поста один из программистов Simcity (2013) рассказал мне, что техника interior mapping использовалась и в этой игре. В ней она выглядит очень здорово, и разработчики записали об этом отличное видео. Они усовершенствовали мою исходную идею, сохранив все текстуры в одну текстуру и добавив комнаты разной глубины. Часть, посвящённая interior mapping, начинается с момента 1:00:
Если вы хотите глубже исследовать эту технику, то можете скачать моё демо interior mapping с исходным кодом. Если вы работаете в Unreal Engine 4, то можете найти interior mapping как стандартную функцию движка в виде функции InteriorCubeMap.
Спустя столько лет очень здорово наконец увидеть, что мою технику interior mapping используют при производстве масштабной видеоигры! Если вам знакомы игры, в которых применяется нечто подобное, то напишите об этом мне.