Рендер нарисованного мира в JG

Речь пойдёт о том, как был сделан нарисованный мир в моей последней демке – JG.

(Click for English version)

Эффект нарисованности не был запланированным. Была идея показать природную сцену определённого типа и мало времени. Стало ясно: пытаться делать реалистично даст либо очень посредственную картинку (ввиду Unity и сложности моделирования растительности для игр), либо год мучений в надежде догнать Crysis (да и тот, на взгляд не привыкшего к графике игр человека, вряд ли выглядит совершенно; картонно-крестовидные плоскости листвы и меня до сих пор коробят). В общем, это был не вариант.

Главное – сохранить правильное ощущение, атмосферу, не испоганив её ограничениями графики. Очень хотелось избежать синтетичности и компьютерности (это же природная сцена всё-таки).

Итак, нужно нарисовать что-то очень сложное, не убив себя и компьютеры игроков.
Интуиция подсказывала: “надо всё запечь”. По крайней мере, с освещением это всегда прокатывало. В данном случае вообще всё сложное, так что и запечь надо всё. Вспомнились 3D-сканы местности: даже при плохой геометрии (или вообще в виде облака точек) они все равно смотрелись достаточно убедительно, из-за того, что все цвета на своих местах, всё со всем сочетается, и детальное реалистичное освещение уже отфильтровано и запечено. К сожалению, время года на момент разработки было прямо противоположно желаемому, так что вариант со сканом отпал.
Но что если мы сделаем реалистичную оффлайн сцену с красивым освещением и получим её “скан”? Где-то в моей памяти всплыл Vue, как нечто, в чём для кино и всяких экстерьерных визуализаций рендерят красивые природные ландшафты. Да, пожалуй это что надо, подумал я.

Покрутив неуклюжий интерфейс, решил для теста воссоздать в Юнити фрагмент какой-нибудь сцены из примеров. Отрендерил её с нескольких ракурсов, сунул в Агисофт и… разочаровался. Сложность геометрии растительности и сглаживание были не лучшими качествами для хорошего скана. Точки еле находились, всё было не на своих местах.
Тут меня осенило. Что делает Агисофт? Он пытается создать несколько карт глубины из картинок, а затем по ним ставит точки. Но ведь Vue сам умеет рендерить точную глубину из камеры, так что зачем мне её восстанавливать?

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

Из этого:

paintprocess1

Некоторыми манипуляциями с MaxScript’ом получаем это:

paintprocess2

Это цельный меш, затекстуренный рендером.

Сложная часть позади, пришло время собрать из таких штуковин сцену и поиграться с шейдером 🙂

Каждый квад поворачивается на камеру и текстурится одним из этих мазков:

daubs

Почти. На самом деле, в шейдере баг, из-за которого местами попадает не целый мазок, а его фрагмент. Однако, исправленная версия мне показалась более скучной и синтетичной, так что я вернул, как было. Это не баг, это фича 🙂

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

Далее я делал в Vue, собственно, нужную мне сцену. Графон выходил такого рода:

pond_v2

Заборчик, дом и ландшафт делались с нуля, растения же практичнее было поискать в готовых паках и собрать из всего этого цельную композицию. Я сбился со счёта, сколько мне потребовалось рендеров, чтобы забить всё играбельное пространство точками:

sdf5

Многие рендеры приходилось дополнительно немного обрабатывать для более “живописного” эффекта – вытягивать больше оттенков, убирать темноту, делать тени немного синее:

pond_300_fill

Сперва я долго не мог подобрать хорошее освещение, и этой черноты, требующей выправления, было много. Затем удалось всё же получать сразу на рендере картинку лучше, но итоговая сцена в игре сшита из рендеров разных времён, что мне даже понравилось, делало её более интересной, менее монотонной.

Некоторые ранние кадры:

К этому моменту, в отличие от первых попыток, уже была реализована смена направления мазков, ибо, лежащие одинаково, они смотрелись очень неестественно и похоже на фильтр из фотошопа. Сперва я понадеялся задать её абсолютно процедурно, но этого не оказалось достаточно. Процедурный вариант находил разницы яркостей соседних пикселей и на основе этого создавал вектор направления – похоже на то, как из карты высот считают карту нормалей. Но местами мне было очевидно, как должны лежать мазки, а шейдеру нет: скажем, я знал, что траву здесь лучше рисовать вертикальными линиями, а забор по направлению палочек самого забора. В итоге я решил рисовать карты направлений для каждого рендера, где цвет задавал вектор, и совмещать это с процедурным направлением вдалеке. Вот так странно выглядели карты направлений:

pond4_dir_hi

Рисовать векторы в фотошопе цветом – то ещё удовольствие (для извращенцев).

Разница довольно очевидна: слева с одинаковым направлением, справа с изменённым:

paintprocess3

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

Домик в конце пришлось практически полностью рисовать кистями в фотошопе, т.к. фотографичная версия слишком выбивалась из общего стиля.

Такие вот дела. Записал всё это, чтобы хотя бы самому не забыть, что и как делал 🙂

 

Бонус:

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

paintprocess4


One comment

  1. Pingback: Rendering painted world in JG « Mr F


Leave a comment