Penumbra shadows

Тут я начну цикл постов по темам того, чем я занимался в своей дипломной работе. Называлась она незатейливо: “Реалистичные материалы в реалтайм рендеринге”, однако под собой это подразумевало всё что угодно от реалистичных теней до избавления мелкого спекуляра от алиасинга.
В целом, задача была – рендерить красивую сцену в реалтайме.

В этом посте я расскажу, что я делал с динамическими тенями.
Тени должны были иметь вариативный радиус полутени – чтобы вблизи кастера они были чёткими, а вдали – размытыми, степень размытости должна варьироваться от физического размера источника.

Я изначально упростил себе задачу – пусть тени отбрасывают только динамические объекты (которых в демке будет немного), а на статике запечём лайтмапы.

Мучавшись с месяц, я родил вот такую демку:
!iengine 2013-02-13 01-10-23-92

!iengine 2013-02-13 01-10-36-45

iengine 2013-02-13 01-18-55-10

Её можно скачать здесь: http://geom.io/iengineShadows.zip

/*
В ini файлике можно поменять разрешение.
 Если у вас не нвидия - снизьте antialiasing, т.к. по умолчанию там нвидия-специфик CSAA.
 Мышь + WASD - летать
 LMB - задать направление света в соответствнии с направлением камеры.
 Колесо мыши - менять размер источника света (т.е. размер пенумбры теней). Идеально чёткими конечно не сделать, т.к. ограничено разрешением шадоумапы.
 Требуется нормальная видеокарта скорее всего.
*/

На что следует обратить внимание в первую очередь, так это на отсутствие шума, так надоевшего мне в тенях многих современных игр и действительно большой радиус размытия (широченные гауссы в реалтайме проблематичны).

Короче говоря, как это работает: за основу я взял технику PCSS, суть которой в нахождении для каждого фрагмента некого среднего значения глубины вокруг него в шадоумапе – это значение конвертируется в радиус размытия, который затем юзается в PCF.

Технику юзали не часто, ибо она тормозила. Поиск средней глубины требовал множество выборок, PCF при большом радиусе – не меньше. Чтобы PCF был широким и гладким, его надо сделать совсем тормозящим, и все равно ещё будет присутствовать алиасинг на поверхностях под острым углом (отсутствие мипмаппинга шадоумапы). Альтернативы – мало семплов и жуткий banding или вышеупомянутый шум. В общем то, в играх научились маскировать этот шум не так уж плохо – проходясь по нему скринспейс блюром. Но намётанный глаз все равно спалит =).

Первым делом я решил заменить PCF на другой алгоритм. Чудесность PCSS в том, что PCF в нём совершенно необязателен – даже при не высоком числе выборок в стадии blocker search, мы получаем не самые кривые коэффициенты размытия, которые можем засунуть в любой алгоритм.

Меня заинтересовали summed area tables. Суть их в том, что благодаря простой арифметике, имея картинку, каждый пиксель которой содержит сумму всех пикселей выше и левее его (существуют вариации и с ниже и правее, но не суть), мы можем найти среднее значение всех пикселей в любом прямоугольнике на ней, имея лишь угловые значения. Сперва это может туго пониматься – но атишная дока довольно наглядна. Таким образом, сделав один раз препасс и превратив любую текстуру в SAT, мы можем за 4 выборки и маленькое число инструкций получить блюр любого радиуса. Ух ты!

Ух ты ли? На самом деле далеко не совсем.

Во-первых, суммы пикселей будут иметь чертовски широкий диапазон значений. Если текстура была RGBA8 формата, для SAT в большинстве случаев придётся создавать RGBA32F. И даже в точность флоатов SAT вносит много погрешностей. Если на цветовой текстуре их может быть не заметно, это может сломать шадоумапы. Я бы не стал юзать SAT для больших теней аля открытый мир, но т.к. в моих планах было маленькое число дин. объектов в мире статики – жить было можно.

Во-вторых, препасс очень тяжёлый. “Сложить все пиксели текстуры” звучит несложно на словах, но совсем не дешёво на практике. Лучший известный способ, он же представленный в атишной доке, требует несколько пассов, причём кол-во пассов очень быстро увеличивается при увеличении текстуры. Генерировать SAT больше, чем на 512х512 – дохлый номер. Дешевле делать VSM с широким блюром.

Но, однако, в вышеобозначенной демке я всё же использовал SAT – ещё не успев окончательно в нём разочароваться.

Были применены некоторые дополнительные трюки:

Дело в том, что у PCSS техники есть один знатный баг – невозможно получить несколько полутеней, пересекающих друг друга корректно – blocker search видит только ближайшие к камере данные из шадоумапы. Поэтому “главной” полутенью будет полутень ближайшего к ней объекта – и если какой-нибудь более мелкий объект стоит в тени и кастует свою тень на полутень объекта за ним – она не отобразится. Будет полутень главного, а потом резко начнётся тень мелкого, как только он появится в шадоумапе.

Пока тени не пересекаются, это не заметно, но я хотел это исправить. Для этого я сделал из шадоумапа атлас, в котором выделил отдельно место для каждого объекта – таким образом я ещё и сэкономил пространство текстуры и смог крутить препасс SAT отдельно на каждый блок атласа. Вообще там было хитро – 512х512 атлас с 4мя шадоумапами по 256х256, я смог генерировать SAT атласа за кол-во пассов, необходимое для одной 256х256 текстуры.

Таким образом, у меня были данные всех объектов в шадоумапе не загороженные и можно было избежать этого артефакта – можно заметить его отсутствие на втором скрине.

Тем временем сроки подходили к концу, на меня снова стал сыпаться контент, и такие экспериментальные методы пришлось отбросить. У меня не было времени подготовить к “продакшену” всю эту систему с атласами.

Дело было упрощено до VSM + PCSS. Шадоумапа рисовалась в VSM текстуру без всяких атласов, далее по ней проходился минимальный блюр. PCSS юзал тот же PCF цикл, который вместо бинарных сравнений/hardware pcf’а семплил эту VSM карту. Минимальный блюр в ней был конечно пошире хардварного псфа, это позволяло брать мало семплов (при псфе это выглядело бы как жуткий бандинг). В результате получались тени с широким (много семплов неширокого) блюром вдали от кастера и менее широким вблизи. В идеале хотелось сделать их вблизи чётче – но и так более-менее устраивало:

btest 2013-05-29 16-45-03-04 1dxc btest 2013-06-19 19-52-11-26

Конечно, эффект уже был не такой, но с ними просто было работать. Тут можно посмотреть видео: http://www.youtube.com/watch?v=2jk5TmfKNZA

Остался, правда, косяк, который я не успел исправить – а именно резкие границы у черезчур размытых теней. Для оптимизации у меня в шейдере стоял if, который не считал тени там где их не должно было быть – но работал он не совсем корректно.

Далее подобным образом я сделал и тени от поинтлайтов – для VSM, их пришлось рендерить как dual-paraboloid‘ы.

Прикольная черта теней от поинт лайтов – т.к. мы снимаем шадоумапы с центра лайта с включённой перспективой – у нас автоматически дальние объекты становятся мельче и тени от них размываются сильнее. Получается дополнительный фейк в пользу визуального эффекта корректной полутени =).

btest 2013-06-01 01-33-28-62 btest 2013-06-01 01-34-11-40 btest 2013-06-05 03-43-43-04 btest 2013-06-03 23-13-26-26

Каковы мои дальнейшие планы?

Мне нравятся distance fields и то, что с ними можно сделать. Даже очень лоуресные DF могут трейситься как довольно похожие на оригинальную форму геометрии – в дипломной работе я использовал их для самоотражений объектов (но об этом в другой раз). Много чего можно запечь в маленькие DF. А можно и попытаться генерить их в реалтайме…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s