Стилизация рендеринга “нарисованного” мира
В этой статье будет небольшой разбор того, как в этом демо (загрузить его можно отсюда) был создан эффект, при котором виртуальное окружение выглядит так, будто его разукрасили масляными красками.
Вот видео:
Эффект нарисованности не был запланированным. Была идея показать природную сцену определённого типа и мало времени. Стало ясно: пытаться делать реалистично даст либо очень посредственную картинку (ввиду Unity и сложности моделирования растительности для игр), либо год мучений в надежде догнать Crysis (да и тот, на взгляд не привыкшего к графике игр человека, вряд ли выглядит совершенно; картонно-крестовидные плоскости листвы и меня до сих пор коробят). В общем, это был не вариант.
Главное – сохранить правильное ощущение, атмосферу, не испоганив её ограничениями графики. Очень хотелось избежать синтетичности и компьютерности (это же природная сцена всё-таки).
Таким образом, я должен был создать что-то, с одной стороны, очень сложное, а с другой, не требующее большой вычислительной мощности (чтоб не спалить компьютеры тех, кто решит установить мою демку). Но что именно? Интуиция прошептала: «Нужно все запечь!» И тут в памяти всплыли 3D-сканы природных пейзажей. Там все цвета расположены как нужно, а освещение отфильтровано, «просчитано» в реальном времени и уже «впечено» в окружение, благодаря чему эти 3D-сканы выглядят довольно убедительно даже с плохой геометрией (или даже будучи просто облаком точек). К несчастью, на тот момент время года было не тем, что мне было нужно, поэтому поупражняться в фотограмметрии не получилось.
Но что если «просканировать» реалистичную офлайн-сцену? Мне вспомнилась программа Vue – с ее помощью специалисты по генерации экстерьеров и кинематографических 3D-сцен создают виртуальные природные пейзажи. Я понятия не имел, что из этого выйдет, но все же решил попробовать.
Я взял одну из сцен-болванок, отрендерил ее с разных углов и поместил результат в Agisoft Photoscan, чтобы воссоздать приблизительную геометрию вместе с «запеченным» в эту сцену освещением. И… увы, результат себя не оправдал. Причины – сложная растительность и эффект сглаживания. И тут меня осенило. Что, по сути, делает Agisoft? Он создает карту глубины, а затем размещает на разных глубинах облака точек. Ну, так я же могу сгенерировать карту глубины прямо в Vue, зачем лезть в Agisoft?
Таким образом, зная техники отложенного просчета и преобразования глубины в позицию, я мог взять сцены из Vue и сделать из них облака точек. Впрочем, сделать это было не так-то просто. У глубин в Vue не очень удобная кодировка, но, к счастью, я нашел решение и этой проблемы.
И из этого:
Немного пошаманив с MaxScript, я получил это:
Т.е. у нас получилась объемная текстурированная сетка.
Самое трудное позади, нужно лишь повторять этот процесс, пока в сцене не останется дырок. Теперь, наконец, можно поиграться с шейдерами! :)
Здесь каждый отображаемый пиксель ведет себя как квадратный полигон, а в качестве текстуры у него используется один из таких мазков:
Почти готово. При просчете был баг, из-за которого на некоторых полигонах была лишь часть мазка. Но это выглядело даже лучше, поэтому я не стал ничего фиксить. Это не баг, а фича! :)
Размер полигонов, разумеется, зависит от глубины, т.е. чем ближе расстояние, тем больше полигон. Было очень важно не смешивать вместе большие и маленькие полигоны, поэтому я очень осторожно выбирал углы обзора.
Тестовый образец выглядел на отлично, поэтому я решил, что пора приступить вот к этой сцене:
Я сделал ее сам. Дом, забор и окружающий пейзаж были созданы с нуля. Растения были добавлены из уже существующих наборов. Я объединил это все в одной композиции, причем растений пришлось отрендерить очень много – так, чтобы они покрывали всю площадь сцены:
Некоторые картинки пришлось подфотошопить – чтобы избавиться от темных пятен и добавить цветов:
Этих темных пятен пришлось подправить очень много, потому что у меня возникли проблемы с созданием правильного освещения. Но в итоге я все же заставил картинку выглядеть лучше. Финальная сцена – это смесь разных подходов, потому что у меня не было времени заново рендерить все с разными настройками. Ну, и выглядело это посимпатичней.
Вот пара ранних скриншотов:
К этому моменту я уже подправил направление мазков – это было важно, поскольку мазки, направленные в одну сторону, выглядели неестественно. Сначала я попытался сделать это через процедурную генерацию (похожим образом из карты высот генерируется карта нормалей), но безуспешно. То есть мне хотелось, чтобы одни мазки лежали одним образом, а другие – другим. К примеру, мне нужно было, чтобы на траве были вертикальные мазки, а на заборе – мазки, повторяющие форму забора. И когда у меня не получилось сделать это через процедурную генерацию, я решил сделать дополнительные текстуры, вручную прорисовав в них нужное направление мазков. В финальной версии у мазков, которые находятся близко к камере, используются текстуры, нарисованные вручную, а у мазков, которые далеко – текстуры, сделанные процедурной генерацией. Вот пара примеров:
По правде говоря, разукрашивать векторы разными цветами в фотошопе – это не самая лучшая идея, но это самый быстрый метод, до которого я смог додуматься.
Но разница очевидна. Слева – мазки, смотрящие в одном направлении, справа – в разных:
Таким образом мы и подходим к финальной картинке. В концовке я решил немного оторваться и воспользоваться тем, что всё состоит из маленьких квадов, заставив их крутиться, разлетаться и собираться по всякому. Вся анимация частиц задана в шейдере.
Домик в конце пришлось практически полностью рисовать кистями в фотошопе, т.к. фотографичная версия слишком выбивалась из общего стиля.
Такие вот дела. Записал всё это, чтобы хотя бы самому не забыть, что и как делал.
Бонус: Недавно меня спрашивали, как заделать дыры между полигонами. Очень просто – базовую геометрию нужно сделать очень примитивной:
Автор Mr F, оригинал найдете по ссылке, Rendering painted world in JG