Как стать автором
Обновить

Комментарии 47

БОЛЬШЕ БОЛЬШЕ БОГУ БОЛЬШЕ!!! Всё интересно, и алгоритмы, и картинки, и видео, и подводные камни!
Чую скоро кирпичи будут с нормалмаппингом и объемным попиксельным освещением.

Спасибо вам за интересную статью. Нового для себя почерпнул мало, но картинки посмотрел с удовольствием.
Нет, скорее всего, normalMapping'а не будет, сколько его не пробовал для пиксельарта, получается два варианта:
1. Пластилиновое, с сильными градиентами нечто, убивающее весь дух пиксельарта;
2. Шумное и нереалистичное нечто.
Честно говоря с пиксельартом не пробовал, не работаю в данном стиле. Нахожу 99% его реализаций сегодня жутким трешем с пикселями разного размера, мерцаниями при движениях и так далее. У вас пока пиксели очень радуют глаз.
Как-то экспериментировал с пиксель артом и PBR. Скажу, что результата добился желаемого, но задача действительно сложная и нормал мапы шлифовал долго.
image
Смотрится очень круто. Поделитесь, что за проект?
Я так понимаю, плащ изначально был отрендерен в 3d?
Клинок коротковатый. Это кинжальчик? Длина одноручного меча должна быть такой что при удерживании в руке остриём вниз, до земли должна оставаться пара сантиметров.
PS хотя конечно зависит от сеттинга: для античности это вполне обычный клинок, а в Средние Века во многих странах, короткие мечи были для простолюдинов, а длинные были исключительно только для дворян (ну и купцам, в виде исключения, при длительном путешествии разрешалось взять с собой седельный меч — носимый пристёгнутым к седлу и запрещённый для пешего использования).
Выглядит чуднО. Возможно из-за черной обводки. Но круто, конечно, мне нравится.
Ого, а как такое отражение сделано? Это в юнити?
nightrain912 посмотрите, когда то давно делал normalMapping для пиксель арта:
https://habrahabr.ru/post/183534/
И большое спасибо за статью, хочется еще и еще)

Да, читал вашу статью, когда начинал этот проект :)
Возможно, если сделать свет более дискретным (round(lightValue * 5) / 5), мне может подойти normalMapping. Но мне кажется, в динамике даже нескольких уровней будет достаточно глазу, чтобы заметить градиентное мыло.
Думал над таким вариантом: делать очень дискретную normal map, всего с несколькими уровнями. А результат normalMapping'а искусственно зашумлять.

А почему вы решили, что обязательно мыло-то будет?

Пробовал пару лет назад что-то подобное :)

Чёрный фон вместо земли всё портит, как по мне. Некоторые локации выглядят просто ляпами на картинке из-за этого.

Возможно в движении всё по другому, конечно, но в статике так.
Дело еще и в размере персонажа: по факту, мы будем видеть несколько десятков пикселей земли максимум. Поэтому будут незаметны большие тёмные зоны.
А где-то можно на это всё в динамике глянуть?

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

Что ж, ждёмс =)
А где видео к этой статье? :)
Боюсь, что видео для статического глобального освещения ничем не будет отличаться от картинки :)
Интересно попробовать просто последовательность размытий неба. Навскидку в фотошопе получилось вот так:



Эффект конечно совсем не тот :) Но зато можно в одном шейдере реализовать))
Это SSAO для «бедных» xD, оно не мешает тому что сделал автор, а вполне может его дополнить.
HBAO тут наверное более уместен. И да, он явно бы дополнил светотень.

Не могли бы вы дать парочку статей по теме HBAO? Гугл в этот раз меня подвёл.

Да, так можно было бы сделать динамическое освещение, но, как видите, оно имеет ряд проблем (особенно это видно слева, в пещере). Изначально я смотрел в эту сторону, попробовал несколько вариантов, не очень понравилось. Хотя, если не предвидится каких то тоннелей, только комнаты, можно добиться классного освещения, не жертвуя динамикой.


Так круто, когда кто-нибудь не просто комментит статьи, но и еще создаёт какой-то контент для коммента, чтобы подчеркнуть своё мнение! :)

Придется конкретно заморочиться с оптимизацией при пересчете света при любом изменении геометрии уровня. Для открывания дверей например.
Можно заранее просчитать маски при изменении геометрии и накладывать их по условиям. Главное, не делать уровни со вложенным комбинаторным взрывом.

Я думал о немного другом пути (и не могли бы вы пояснить идею с масками?):
По сути, можно рендерить освещение только для текущей области видимости в текстуру/кеш. Это могло бы позволить делать уровни любого размера (сейчас размер очень ограничен), даже добавлять динамику. Но появятся ограничения:


  • Нельзя будет сильно отдалять камеру (хотя можно перерассчитывать свет с меньшим масштабом, но артефакты будут сразу видны)
  • возможно, не получится добиться высокой производительности: сейчас вся фишка алгоритма в том, что мы не делаем raycast'ы для непрямого освещения (т.к уже есть все данные), а если считать только для экрана — придётся их делать, причем достаточно много.
Вот, смотрите, у вас статическое ненаправленное освещение и направленное дальше 3-4 шага отражений, в принципе, не особо будут изменяться с изменением направления освещения. Первое можно в принципе посчитать один раз и забыть (или два, при минимальной и максимальной силе освещения), второе считать в динамике первые 3 шага, а остальное считать из 5-10 заранее посчитанных масок для разных углов, плавно накладывая их друг на друга умножением. Их разница и общий вклад уже не будут столь важны, чтобы кто-то не особо въедливый заметил, да и глубину для экономии ресурсов можно значительно уменьшить.
Но я скорее писал о геометрии, если её изменение можно предсказывать, а далеко не все игры предполагают обратное, можно заранее посчитать освещение до и после и сохранить в памяти дельту. Это касается каких-нибудь дверей, ставней, источников света и тп.

Поэтому у меня нет дверей :)
А если серьезно — да, несколько секунд расчетов — это неприемлемо для динамического освещения. Просто для интереса: я выкрутил на минимум настройки света и подвигал блоки, которые должны быть static (у меня такие вещи вызывают перерасчет метаинформации о уровне, для редактора). Смотрится дико круто. Но, даже и минимальными настройками (масштабом 4 или 8, и меньшим количеством лучей) притормаживает. :(
Всё-таки, алгоритм был изначально направлен на статику.

Можно рассмотреть вариант:
1. Определить конкретную геометрию как динамическую (только присутствие\отсутствие).
2. Разделить результат расчета света на несколько слоев, для каждого луча или группы.
3. При пересечении лучом динамической геометрии Считать сразу 2 варианта. С учетом отражения и с учетом прохода сквозь.
4. В рантайме в зависимости от вкл\выкл динамического объекта использовать один или другой результат расчета слоев.
5. Перед самим рендером из набора необходимых для кадра слоев собрать текущую карту освещенности.
Правда при большом количестве коллизий будет задействовано очень много видеопамяти, нужно дополнительно упаковывать или обрезать результаты расчетов света для лучей\групп.

Кстати, неплохая идея. Интересно только, как обеспечить всего 2 текстуры: по идее, для каждого динамического объекта придется считать отдельную текстуру, разве нет?

К сожалению этого будет недостаточно, будут артефакты и засветы. Все из-за того что в некоторых случаях пиксели будут подсвечиваться и прямым и отраженным светом и накладываться на освещение только с прямым светом. Если ввести ограничение на количество отражений, то можно интенсивность освещения для каждой итерации записывать в свой цветовой диапазон. Тогда можно проходить для пикселя примерно так:
— смотрим освещенность из главного диапазона, освещаем ей.
— сморим освещенность из менее приоритетного диапазона (первое отражение) и досветляем если значение больше чем в первой итерации, и не засветляем если больше.
и так до окончания диапазонов. Экономим n текстур,

Но вообще все это сложно, и не эффективно, для динамического освещения нужно исследовать в другую сторону. Данные алгоритмы пригодны только для красивой статики, лепить поддержку динамики не продуктивно. ИМХО.
Не приходило в голову идеи при первичной трассировке проходить не попиксельно все мягкие точки с фиксированным углом, а похожим способом, как вы делали в первой статье: построением лучей (скорее, прямых) между двумя углами грунта, или, точнее, между границами и углами фона\стен. Тогда можно будет отсечь освещённые\неосвещённые первичными лучами пиксели без погрешности, которая появляется при фиксированных углах трассировки и выглядит, порой, мерзотно (в смысле несоответствия геометрии).
Если описывать алгоритм словами: предварительно выделяем все координаты твёрдых углов и перекрываемые квадранты, затем для каждой точки границы пускаем лучи (для оптимизации, вычислять квадранты направления и пускать только в те, которые не перекрывают луч твёрдым квадрантом-границей), и, если луч упирается в границу с небом, работать с яркостью.

Я примерно трижды прочел ваш комментарий, и, к своему стыду, так и не понял идею алгоритма. :(
Вы не могли бы как-нибудь визуализировать (да хоть на листке бумаги) его? Заранее спасибо большущее.

Да, действительно бредово описал. Попробую проще.
Вы в первой статье описывали динамическое освещение через построение мешей по вершинам — углам. А сейчас отошли от этой идеи в пользу наивной трассировки лучей с фиксированным шагом угла. Я, весьма сумбурно спросил, а почему, собственно, нужно было отходить в пользу монструозной трассировки, если можно построить эти самые меши для каждого источника света, включая бесконечно удалённый, посчитать смесь наложений в областях как первичную яркость, а грани твёрдых поверхностей представить в качестве вторичных излучателей. А когда с непосредственно трассировкой лучей будет покончено, сложить все яркости и применить рассеивание света. На слух это не больший ужос, чем 7 раз проходить через всю огромную текстуру уровня, бросая по 25 лучей из каждой точки. (Кстати, имеет смысл каждый проход поворачивать бросаемые вектора на delta*i/(steps+1), избавитесь от явных погрешностей, вроде несовпадение размеров лучей из узких бойниц)
В голосовании не хватает пункта: «И так все потрясающе!»)

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

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

На самом деле, от своих знакомых я получал в том числе и такой фидбек:
"Красивые картинки, но ни черта не понятно";
"Картинок и текста много, а вот кода не хватает, было бы круто выложить код шейдеров и кое-каких классов";
"Очень не хватает uml диаграммы или чего-то подобного, чтобы понять в целом структуру проекта";
"Очень сухой стиль изложения и много ненужных подробностей, как в курсовой работе".


Так что есть над чем работать и именно для этого нужны прям конкретные (и, может быть, злые) замечания :)

Для прямого освещение используете только верхнюю полусферу (что вполне логично), но для не прямого возможно уже есть смысл на 360 градусов смотреть?
Или это получается как-то автоматом, например при пересечении со стеной две точки «связываются» вместе для не прямого освещения друг друга.

Знаете, а я накосячил в статье. Изначально, действительно, работал только с верхней полусферой, но в какой-то момент оказалось, что свет получается не очень естественный. Причем, т.к непрямое освещение не использует raycast'инг, а берет направления из прямого освещения, пришлось работать со всей сферой целиком.


Вот так получается при сфере:



А вот так при полусфере:



Спасибо за комментарий, подправлю текст статьи :)

А это точно не ambient occlusion?

Ну, можно и так сказать. Но в AO ведь рассчитывается только прямая освещенность точки? В то время как тут учитываются переотражения.

AO это симуляция GI.
Конкретных алгоритмов я не знаю, но там расчитывается затенённость именно по рассеянному свету.

Вот тут есть описание хитростей и фокусов AO 80ого уровня:
https://renderman.pixar.com/view/production-ready-global-illumination
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории