Комментарии 47
Спасибо вам за интересную статью. Нового для себя почерпнул мало, но картинки посмотрел с удовольствием.
1. Пластилиновое, с сильными градиентами нечто, убивающее весь дух пиксельарта;
2. Шумное и нереалистичное нечто.
Я так понимаю, плащ изначально был отрендерен в 3d?
https://habrahabr.ru/post/183534/
И большое спасибо за статью, хочется еще и еще)
Да, читал вашу статью, когда начинал этот проект :)
Возможно, если сделать свет более дискретным (round(lightValue * 5) / 5), мне может подойти normalMapping. Но мне кажется, в динамике даже нескольких уровней будет достаточно глазу, чтобы заметить градиентное мыло.
Думал над таким вариантом: делать очень дискретную normal map, всего с несколькими уровнями. А результат normalMapping'а искусственно зашумлять.
Возможно в движении всё по другому, конечно, но в статике так.
Эффект конечно совсем не тот :) Но зато можно в одном шейдере реализовать))
Да, так можно было бы сделать динамическое освещение, но, как видите, оно имеет ряд проблем (особенно это видно слева, в пещере). Изначально я смотрел в эту сторону, попробовал несколько вариантов, не очень понравилось. Хотя, если не предвидится каких то тоннелей, только комнаты, можно добиться классного освещения, не жертвуя динамикой.
Так круто, когда кто-нибудь не просто комментит статьи, но и еще создаёт какой-то контент для коммента, чтобы подчеркнуть своё мнение! :)
Я думал о немного другом пути (и не могли бы вы пояснить идею с масками?):
По сути, можно рендерить освещение только для текущей области видимости в текстуру/кеш. Это могло бы позволить делать уровни любого размера (сейчас размер очень ограничен), даже добавлять динамику. Но появятся ограничения:
- Нельзя будет сильно отдалять камеру (хотя можно перерассчитывать свет с меньшим масштабом, но артефакты будут сразу видны)
- возможно, не получится добиться высокой производительности: сейчас вся фишка алгоритма в том, что мы не делаем raycast'ы для непрямого освещения (т.к уже есть все данные), а если считать только для экрана — придётся их делать, причем достаточно много.
Но я скорее писал о геометрии, если её изменение можно предсказывать, а далеко не все игры предполагают обратное, можно заранее посчитать освещение до и после и сохранить в памяти дельту. Это касается каких-нибудь дверей, ставней, источников света и тп.
Поэтому у меня нет дверей :)
А если серьезно — да, несколько секунд расчетов — это неприемлемо для динамического освещения. Просто для интереса: я выкрутил на минимум настройки света и подвигал блоки, которые должны быть static (у меня такие вещи вызывают перерасчет метаинформации о уровне, для редактора). Смотрится дико круто. Но, даже и минимальными настройками (масштабом 4 или 8, и меньшим количеством лучей) притормаживает. :(
Всё-таки, алгоритм был изначально направлен на статику.
1. Определить конкретную геометрию как динамическую (только присутствие\отсутствие).
2. Разделить результат расчета света на несколько слоев, для каждого луча или группы.
3. При пересечении лучом динамической геометрии Считать сразу 2 варианта. С учетом отражения и с учетом прохода сквозь.
4. В рантайме в зависимости от вкл\выкл динамического объекта использовать один или другой результат расчета слоев.
5. Перед самим рендером из набора необходимых для кадра слоев собрать текущую карту освещенности.
Правда при большом количестве коллизий будет задействовано очень много видеопамяти, нужно дополнительно упаковывать или обрезать результаты расчетов света для лучей\групп.
Кстати, неплохая идея. Интересно только, как обеспечить всего 2 текстуры: по идее, для каждого динамического объекта придется считать отдельную текстуру, разве нет?
— смотрим освещенность из главного диапазона, освещаем ей.
— сморим освещенность из менее приоритетного диапазона (первое отражение) и досветляем если значение больше чем в первой итерации, и не засветляем если больше.
и так до окончания диапазонов. Экономим n текстур,
Но вообще все это сложно, и не эффективно, для динамического освещения нужно исследовать в другую сторону. Данные алгоритмы пригодны только для красивой статики, лепить поддержку динамики не продуктивно. ИМХО.
Если описывать алгоритм словами: предварительно выделяем все координаты твёрдых углов и перекрываемые квадранты, затем для каждой точки границы пускаем лучи (для оптимизации, вычислять квадранты направления и пускать только в те, которые не перекрывают луч твёрдым квадрантом-границей), и, если луч упирается в границу с небом, работать с яркостью.
Я примерно трижды прочел ваш комментарий, и, к своему стыду, так и не понял идею алгоритма. :(
Вы не могли бы как-нибудь визуализировать (да хоть на листке бумаги) его? Заранее спасибо большущее.
Вы в первой статье описывали динамическое освещение через построение мешей по вершинам — углам. А сейчас отошли от этой идеи в пользу наивной трассировки лучей с фиксированным шагом угла. Я, весьма сумбурно спросил, а почему, собственно, нужно было отходить в пользу монструозной трассировки, если можно построить эти самые меши для каждого источника света, включая бесконечно удалённый, посчитать смесь наложений в областях как первичную яркость, а грани твёрдых поверхностей представить в качестве вторичных излучателей. А когда с непосредственно трассировкой лучей будет покончено, сложить все яркости и применить рассеивание света. На слух это не больший ужос, чем 7 раз проходить через всю огромную текстуру уровня, бросая по 25 лучей из каждой точки. (Кстати, имеет смысл каждый проход поворачивать бросаемые вектора на delta*i/(steps+1), избавитесь от явных погрешностей, вроде несовпадение размеров лучей из узких бойниц)
Нет, если бы я хотел себя нахваливать, мог бы и без статей обойтись. А сюда пришел поделится опытом и получить фидбек (потому что иногда возникает ощущение, что делаю какую то хрень :) ).
Но спасибо за комплимент.
На самом деле, от своих знакомых я получал в том числе и такой фидбек:
"Красивые картинки, но ни черта не понятно";
"Картинок и текста много, а вот кода не хватает, было бы круто выложить код шейдеров и кое-каких классов";
"Очень не хватает uml диаграммы или чего-то подобного, чтобы понять в целом структуру проекта";
"Очень сухой стиль изложения и много ненужных подробностей, как в курсовой работе".
Так что есть над чем работать и именно для этого нужны прям конкретные (и, может быть, злые) замечания :)
Или это получается как-то автоматом, например при пересечении со стеной две точки «связываются» вместе для не прямого освещения друг друга.
Знаете, а я накосячил в статье. Изначально, действительно, работал только с верхней полусферой, но в какой-то момент оказалось, что свет получается не очень естественный. Причем, т.к непрямое освещение не использует raycast'инг, а берет направления из прямого освещения, пришлось работать со всей сферой целиком.
Вот так получается при сфере:
А вот так при полусфере:
Спасибо за комментарий, подправлю текст статьи :)
Ну, можно и так сказать. Но в AO ведь рассчитывается только прямая освещенность точки? В то время как тут учитываются переотражения.
2D магия в деталях. Часть третья. Глобальное освещение