Создание точечного источника света с помощью многоугольника видимости
Технические подобности реализации системы освещения в 2D-игре.
Французский инди-разработчик Пьер Вигьер в своём блоге подробно описывает процесс создания собственной RPG Vagabond. Автор детально разбирает технические аспекты каждого элемента игры, поэтому его пример может быть полезен начинающим разработчикам. В одном из постов Пьер уделил внимание созданию системы освещения в 2D-пространстве — мы выбрали из публикации самое главное.
Задача Пьера — симулировать цикл смены дня и ночи, а также сделать походы в подземелья атмосфернее. Для этого он и решил создать адекватную систему освещения.
Система Пьера использует две текстуры: на первой изображены все видимые объекты, а на второй — рассеянный свет. Затем на второй текстуре разработчик отрисовывает каждый цвет с помощью аддитивного блендинга (метод, с помощью которого смешиваются лучи света каждого отдельного источника). После этого он накладывает текстуру освещения с мультипликативным блендингом.
На данный момент система освещения Пьера поддерживает точечные источники света. У них есть несколько показателей — позиция, радиус, цвет и интенсивность. Для каждого источника света на экране разработчик рисует квадрат, центр которого совпадает с самим источникам, а длина его сторон равна 2r (r — радиус освещённой области). Далее он использует шейдер, чтобы определить цвет каждого пикселя в квадрате.
Следующий шаг — выбор формулы для описания цвета, получаемого каждой точкой от источника света. Поскольку точечный источник света одинаково освещает окружение во всех направлениях, полученный цвет зависит только от расстояния. Следовательно, можно использовать следующую формулу.
Изначально Пьер использовал другую формулу для ослабления — она предназначалась для точечных источников света в 3D-пространстве.
К сожалению, эта формула плохо подходит, потому что по мере удаления от источника яркость снижается слишком сильно. Более того, она никогда не равна нулю, поэтому на границе квадрата возникает очевидный контраст.
Разработчик старался менять параметры, но всё, чего ему удалось достичь — снижение яркости источника света. Он стремился к другому, поэтому продолжил экспериментировать. Его следующий шаг — простое линейное ослабление.
Главный плюс этой формулы — освещённость пикселей за пределами радиуса равна нулю. Но затухание возле границы круга всё ещё немного жёсткое.
Чтобы получить более плавное затухание, Пьер просто возводит в квадрат предыдущую формулу, чтобы получить квадратичное ослабление.
Такой результат выглядит значительно лучше.
Наконец, Пьер советует использовать текстуру, которая поддерживает HDR с tone mapping. В ином случае отображение цвета будет зависеть от аддитивного блендинга.
Следующий шаг — создание теней. Для учёта препятствий нужна более сложная формула — она вычисляет точки, которые находятся на лучах, исходящих из источника света — такой подход называется многоугольником видимости.
После вычисления многоугольника видимости нужно просто отрендерить его с помощью шейдера, чтобы получить тени:
А вот пример в пещере.
И ещё видео процесса дебаггинга, в котором можно наглядно увидеть правильность выполнения всех функций.
В игре Пьера будут и другие источники света, но точечные строятся по описанным правилам.
Когда них*я не понял, но выглядит потрясно!
Когда все понял и понял, что здесь описаны очевидные вещи.
Но да - гифки прикольные.
Да не выглядит оно потрясно. У него очень острые тени, будто вырезанные ножницами. В реальной жизни они выглядят куда мягче. Посмотрите на пример Олега пару сообщениями ниже.
Простым языком - чтобы свет не выглядел кружком поверх стен, расчитывается какие его части надо отсечь.
Подобное использовалось в старой игрушке NOX
Кстати, вот пример отличного точечного 2d-освещения на пиксельных шейдерах:
https://www.shadertoy.com/view/4dfXDn
Когда делаешь тень/видимость нужно думать от обратного, надо строить не многоугольник видимости, а четырехугольники невидимости, сложность твоих расчетов уменьшится до О(n), никаких рейкастов, никаких сортировок, просто по информации о сегментах составляешь большую мешку из экстраполированных отрезков в бесконечность и красишь, не закрашенная область и будет областью видимости