Ближе к концу превосходной ленты Вима Вендерса «Идеальные дни» главный герой Хираяма пьёт пиво под мостом после того, как увидел, как Бизнесмен ухаживает за девушкой, в которую влюблён Хираяма. Внезапно к нему подходит Бизнесмен. Всё оказалось не так просто, но их беседа приводит их к фундаментальным вопросам:
Бизнесмен: Тени. Становятся ли они темнее, когда накладываются друг на друга?
Хираяма: Не знаю.
Бизнесмен: Я не знаю ещё очень многого... Именно так заканчивается жизнь... Наверно.
Хираяма: Давайте узнаем прямо сейчас.
Бизнесмен: Что?
И они выходят на свет уличного фонаря, чтобы изучить свои тени (сцена целиком):
Хотя Бизнесмен и не видит разницы, Хираяма уверен, что пересекающиеся тени действительно становятся темнее. «Они должны становиться темнее, это логично». Очень сильная сцена.
К сожалению, Хираяма ошибается. Тени не становились темнее. Источник света всего один, и он довольно далеко, поэтому тень — это просто отсутствие света. Не важно, сколько раз перекрыт источник света.
В 3D-видеоиграх же тени — это нечто совсем иное. Очень легко нарисовать тёмное пятно под ногами персонажа и предполагать, что всё остальное освещено. Возможно, Хираяма вспоминал тень-пятно из Metal Gear Solid, которая становится темнее, когда накладывается на другие?
В реальном мире тени просто существуют, но в играх они разрабатываются и проектируются. Они должны обеспечивать хорошую производительность, но при этом прилично выглядеть. Эта взаимосвязь кажется мне восхитительной, и в статье я объясню, почему. Начнём с простых примеров.
Экранные 2D-тени
Можно отрисовать на экране изображение тени до отрисовки персонажа. Я говорю не о спрайтах теней, как в Duke Nukem 3D, а в буквальном смысле о 2D-изображении без масштабирования. Это срабатывает, если персонаж находится на самом переднем плане, как в Winter Gold или в MDK.
Как я и говорил, это было просто.
Тень-пятно
Так, а теперь 3D. Отрисуем тёмный диск под персонажем. Вот и всё.
На самом деле, нужно ещё и выровнять диск тени относительно земли, а также подумать, как обрабатывать ситуации, когда тень накладывается на уступ. Например, в Super Mario 64 пятна отрисовываются при помощи особой аппаратной функции, которая, по сути, усекает тень, чтобы она отображалась только на плоскости земли.
Тень-пятно может быть и анимированной. В Super Mario 64 она становится меньше при прыжке, а в Metal Gear Solid она меняет форму. Если вы хотите заморочиться, то можете решить проблему тени над выступом, проецируя четырёхугольник тени как декаль.
Проецируемые тени с текстурой для рендеринга
Пятно — это просто текстура, а на текстуры обычно можно выполнять рендеринг во время выполнения. Так что будем рендерить персонажа сверху и использовать это в качестве тени вместо тёмного круга. Это замечательно работает в Crash 3 (видео), но не очень хорошо в Soldier of Fortune, потому что разработчики оставили разрешение тени очень низким.
Стоит отметить, что это отличается от техники shadow mapping, при которой карта глубин рендерится с точки зрения источника света. Здесь мы рендерим только чёрно-белое изображение, используемое как текстура. То есть в каком-то смысле мы говорим об однобитной shadow map.
Как можно сделать тени резче?
Проецируемые тени с геометрией
Сразу приходит в голову такой вариант: «разгладить» отбрасывающий тень объект на плоскости, спроецировав его от источника света. Затем отрендерить его во второй раз, но уже в чёрном цвете. Такие тени обычно оставляют непрозрачными, чтобы скрыть, как части объекта отрисовываются одна поверх другой. Естественно, тень будет корректна только на плоской поверхности пола.
В некоторых ранних лётных симуляторах плоская тень в проекции сверху рендерилась на взлётной полосе. В процессе своих исследований я надеялся найти примеры того, что тень видна и во время полёта, но не смог обнаружить ни одного.
Визуально они выглядят как чёрные стенсил-тени, отбрасываемые на плоскую поверхность.
Тени на рельефе
Игра Virus (1987 год) Дэвида Брэбена на Acorn Archimedes и других домашних компьютерах отрисовывает космические корабли, отбрасывающие на рельеф тени с проекцией сверху.
Более сложный пример — это Interstate ’76. Разработчики изгибали и растягивали плоскую тень, чтобы она соответствовала наклону поверхности. Тени иногда проходят сквозь землю, но в целом эффект довольно правдоподобный. Любопытно, что в показанном ниже программно отрендеренном скриншоте тени слегка прозрачные, а при аппаратном ускорении они полностью чёрные.
Разработчики даже осмелились попробовать проецировать тени больших объектов наподобие мостов, и это им успешно удалось.
Но как отбрасывают тени в любом типе сцены?
Падающая тень на проецируемой текстуре
Этот подход имеет много общего с методикой «Проецируемые тени с текстурой для рендеринга», но работает на поверхностях любой формы. Игра рендерит текстуру тени из проекции сверху, но не отображает её на плоской поверхности, а проецирует текстуру на другие объекты. Можно представить, что это бэт-сигнал, но направленный прямо вниз с неба.
Подобные тени можно сделать очень резкими, но они могут выглядеть странно на вертикальных поверхностях и иногда даже отображаются на потолках. См. видео геймплея Sonic Adventure 2: Battle (2001 год, GameCube).
Кроме того, эта техника отлично подходит для деревьев:
Проецированные тени могут просвечивать сквозь объекты, поэтому применимы лишь в особых случаях. Shadow map (карты теней) можно использовать где угодно.
Shadow map
Этот подход обычно используется для реализации теней. Игра отрисовывает изображение глубин (shadow map) с точки зрения источника освещения, и выполняет чтение из этого изображения при рендеринге мира. Это легко сделать, поскольку можно заново использовать обычный код рендеринга игрового движка.
Ограниченное разрешение shadow map приводит к появлению хорошо известных артефактов с забавными названиями, например, Peter Panning и «теневое акне». Было предложено множество трюков для выделения большей площади shadow map поверхностям, находящимся рядом с камерой, где дополнительное разрешение нужно больше всего. Обычно для красивого отображения необходима некоторая настройка shadow map.
До того, как shadow map стали доминирующей технологией, у них был популярный конкурент.
Стенсил-тени
Ранее популярный способ реализации теней. Стенсил-тени отрисовывают резкие тени на любом типе поверхности. Они создают уникальную атмосферу в стиле фильмов жанра «нуар», который сложно имитировать при помощи shadow map. Самый известный пример — это, разумеется, Doom 3 с его тёмными помещениями:
Стенсил-тени основаны на принципе объёмов теней (shadow volume) — невидимой геометрии, разбивающей мир на освещённые и затенённые пространства. Игра применяет освещение только к тем пикселям, которые не находятся в объёме теней.
Чтобы стенсил-тени работали, мир необходимо перерисовывать множество раз. Если упростить, то игра сначала отрисовывает весь мир с освещением окружающей среды (ambient lighting). Затем для каждого источника света создаются все объёмы тени, за которыми снова следует весь мир, задействуя только незатенённые пиксели. Объёмы отрисовываются разными стенсил-операциями для передних и задних граней. То есть приходится отрисовывать очень большое количество пикселей.
Наверно, самой первой выпущенной игрой со стенсил-тенями стала Severance: Blade of Darkness (2001 год), где тени выглядят великолепно.
Читая обзор игры в британской версии журнала Edge за март 2001 года (pdf), становится очевидно, что, несмотря на графический прогресс, мир в те времена ещё не был готов к «соулслайкам».
Сегодня стенсил-тени особо не используются. Одна из причин этого заключается в непредсказуемых затратах во время выполнения. Эти затраты зависят от величины объёма тени на экране, а значит, могут сильно варьироваться. Кроме того, был запатентован оптимизированный алгоритм. Для разработки Doom 3 Id Software, похоже, заключила некий договор.
Мягкие стенсил-тени
Стенсил-тени не обязаны быть резкими. В игре 2001 года Silent Hill 2 для PlayStation 2, как показано выше, стенсил-тени размываются. На консоли это выглядит почти идеально.
Тени упрощённых персонажей
А что, если тени будут отбрасываться более простой моделью, чем та, которая видна на экране? Например, в Zelda на Nintendo 64 ноги Линка отбрасывают тени, даже если ничто их не отбрасывает:
Уникальный подход использован в Hyperblade — игроки на футуристической хоккейной арене отбрасывают проецируемые тени в виде простых анимированных фигур.
Тени в статическом освещении уровней
Для фиксации освещения на уровнях игры можно использовать техники цветов вершин и карт освещения (lightmap). Они применялись во многих играх как единственный способ отображения крупных теней, поэтому я и добавил их в статью.
Цвета вершин
В Ico видно, насколько сложными могут выглядеть тени при обычном старомодном вершинном освещении.
На низкополигональных картах даже резкие тени можно представить в виде цветов вершин. Отличным примером этого может служить Tony Hawk Pro Skater 2 (2000 год, Playstation), которая выглядела великолепно, учитывая простоту техники.
Карты освещения
Карты освещения — это классический способ хранения освещения и теней уровней. Вместо того, чтобы хранить цвет для каждой вершины, мы создаём второй набор текстур, хранящий только освещение. Их разрешение может варьироваться в зависимости от областей игры, благодаря чему в нужных местах тени способны быть более точными. С другой стороны, для карт освещения требуется больше памяти, чем для цветов вершин.
Карты освещения были популяризированы игрой Quake (1996 год, PC), где выглядели так: https://jbush001.github.io/2015/06/11/quake-lightmaps.html.
На этом мы закончим изучение традиционных методик создания теней. А теперь давайте немного поговорим об освещении в целом.
Тени в современных играх
По возможности в современных играх используются традиционные техники. Вот некоторые из примеров:
Разновидности shadow mapping, например, Cascaded Shadow Maps, позволяющие покрывать большие площади, сохраняя при этом высокую скорость.
Карты освещения в сочетании с другими техниками, например, light probe. В Call of Duty по-прежнему используются карты освещения, см. слайды Hemispherical Lighting Insights.
Идея упрощённой модели персонажа. В The Last of Us (2013 год, PlayStation 3) мягкие тени персонажей отбрасываются при помощи растянутых сфер. См. слайд из доклада Lighting Technology of The Last Of Us (2013). Кроме того, Unreal Engine поддерживает упрощённые капсульные тени для персонажей.
Проецируемые тени. В Hot Wheels Track Attack (2010 год, Wii) меши теней рендерятся на текстуру, а затем она проецируется на гоночную трассу, как описывается в блоге одного разработчика. В движении игра выглядит великолепно.
Тени, получаемые трассировкой лучей
В начале статьи мы сказали, что тени образуются отсутствием света. Если игра действительно пытается симулировать физически корректное освещение, то тени должны выглядеть естественно. Даже маленькие геометрические детали отбрасывают точные тени, в отличие случая с shadow map. Большие лампы естественным образом создают мягкие тени, а непрямое освещение делает светлее тёмные уголки. Для реализации всего этого потребовалось потратить невероятное количество времени и денег на разработку алгоритмов и оборудования трассировки лучей (ray tracing).
На практике современные игры имеют такие сложные сцены, что описанное выше симулируемое решение необходимо аппроксимировать. Например, в получаемых трассировкой лучей тенях Alan Wake 2 (2023 год) каждый пиксель принимает освещение только от одного случайно выбранного источника. Далее результат подаётся алгоритму устранения шумов, который интеллектуальным образом сглаживает шумную картинку. Подробности см. в презентации. Следовательно, даже полученные трассировкой лучей тени не будут «идеальными» и в зависимости от выбранных компромиссов обладают собственным уникальным внешним видом.
И под конец приведём ещё один очевидный вариант.
Отсутствие теней
Иногда тени для вас не главное.
Когда Хираяма в сцене из фильма внимательно изучает тени, его новый друг замечает:
Бизнесмен: Да тебе это действительно нравится.
Думаю, мы, как любители компьютерной графики, разделяем его увлечение.
Все скриншоты взяты с MobyGames, если не указано иное. Благодарю mankeli за подробные примечания по первому черновику статьи. Выражаю благодарность noby, msqrt, shaiggon и Warma за отзывы.
Комментарии (9)
Treviz
31.10.2024 12:07С Doom3, насколько я помню, была такая история. Кармак написал движок: графику, физику, звук. Звуковой софтварный движок, кстати, очень неплохой получился. Но Креатив с патентом на тени (они когда-то и видюхи выпускали, с тех времён осталось) пригрозила судом. В рамках досудебного урегулирования добавили в игру поддержку X-Fi с EAX. Причём внедрением по сути занималась сама Креатив: Кармак их недолюбливал. Заодно перелопатили весь OpenAL.
barnes
31.10.2024 12:07в квейковских движках можно было включить стецил тест и получить полупрозрачную тень без багов.
DimPal
31.10.2024 12:07Тема интересная, но статья выглядит как поток сознания (путано и не последовательно). Полностью забыто что есть мягкие тени. Light map - это метод кэширования освещения (включая вторичку), а не генерации теней. Самопересекающися тени это либо результат оптимизации по скорости, либо непонимание как сделать правильно (хотя зависит от постановки задачи). Все методы имеют разнве артефакты при наивной реализации, методы борьбы с ними - действительно интересная, но узкая тема.
da-nie
31.10.2024 12:07И получалось так:
DimPal
31.10.2024 12:07А если там скелетно анимированные монстры побегут на десяток тысяч полигонов, для их теней производительности CPU хватит всё пересчитать на лету?
da-nie
31.10.2024 12:07Так как эти тени статические в смысле их расположения (и динамические по изменению цветов и яркости источников света) и рассчитываются на этапе подготовки карты, то эти монстры никак не участвуют в отбрасывании собственной тени на поверхности.
ArkadiyMak
31.10.2024 12:07В Half-Life 2 (2004 год, PC) shadow map использовались для персонажей.
Это не так, в хл2 были проекционные тени.The Elder Scrolls IV: Oblivion (2006 год, Xbox 360) имела красивые спроецированные тени деревьев.
Это не так, на геометрию под деревьями накладывалась заранее нарисованная тайлящаяся текстура. Деревья не рендерились в проекционную текстуру, со спидтришными спрайтовыми листьями это вообще было бы проблематично сделать.
Maximov_psy
Ничего не понятно, но очень интересно!