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

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

Мне кажется должен быть способ взять уровень освещенности напрямую с объекта-сферы. без использования камеры.
С помощью рейкастов от игрока до источников, только без коллайдеров.
Коллайдеры, как я понимаю, нужны для оптимизации. Чтобы не делать лишних рейкастов до источников освещения, под свет которых игрок явно не попадает. Когда игрок попадает в зону триггера — добавлять источник освещения в список проверяемых. Нельзя забывать, что рейкаст всё же далеко не самая быстрая операция.
Или использовать квадратный корень из суммы квадратов разностей координат. Тогда каждый источник проверяется за 2 строчки скрипта. Это ещё быстрее. Хотя, мне кажется, будет правильным напрямую получать карту освещённости, как написано ниже.
Во-первых, квадратный корень — дорогая операция, если сравнивать по расстоянию — лучше отдельно держать квадрат расстояния/радиуса и сравнивать сумму квадратов разностей координат с квадратом радиуса.

Во-вторых, не для каждого источника света подойдёт сферическая область. Есть же и другие типы источников света, кроме точечных. Или может быть достоверно известно, что сцена отсекает часть освещаемого объёма, и в этом случае можно освещённый объём принять за куб, например. Тогда придётся писать свой набор абстракций для предварительной проверки возможности источника света осветить конкретную координату.

В-третьих, объект, который освещаем, тоже может быть разной формы, и нужно учитывать и его форму и размеры в том числе. Если pivot персонажа (который может быть как центром тяжести, так и точкой под ногами) не освещён, это ещё не значит, что весь персонаж в тени.

Если «ловить освещение» со сцены (не важно, подхватывать из отрисованного кадра, или из отдельного Render to texture), то можно отхватить неожиданных багов. Например, берём пиксел со спины персонажа (камера находится за спиной персонажа), он тёмный — считаем, что персонаж в темноте. А на самом деле у персонажа там тёмный рюкзак. Или персонаж в светлом, но между ним и камерой чёрный столб. Или персонаж стоит лицом к источнику света и спина у него тёмная; но при этом всё равно неправильно считать, что персонаж спрятался в тени. Спереди его может быть отлично видно.

Опять же, в статье мало сказано о варианте с Render to texture. Если мы рисуем маленькую сферу в центре персонажа, чтобы оценить его освещенённость, то необходимо, чтобы сфера была на отдельном слое, который не будет отрисовываться на главной камере; а персонаж (или даже все персонажи) не должны отрисовываться при оценке освещённости. Иначе probe («маленькая сфера») будет всегда затенён персонажем. Так же будет хорошей идеей убрать из отрисовки на оценку освещённости незначимые объекты (не влияющие на результат):
  • декали и частицы, которые лишь украшают поверхности, но не влияют на форму и свет;
  • skybox и прочие далеко расположенные декорации;
  • мелкие объекты, которые не могут влиять на результат (нельзя спрятаться за копьём или шваброй).
Сферы тут вообще не причём — это лишняя трата ресурсов. Надо знать расстояние и перекрытие прямой между объектом и источником. Правда, для diractional придётся направлять луч противоположно направлено и на большое расстояние. Но это не критично.
Объект можно аппроксимировать, например, сферой или цилиндром. Тогда луч надо начинать не от самого игрока, а чуть подальше.
По мне, так использовать дополнительную камеру — это какое-то чрезмерное извращение. Я думаю, что наверняка эта проблема решается в шейдерах. Но я не знаю, как их писать. Поэтому предлагаю использовать обычные скрипты как альтернативу.

Кошмарные костыли. Надо при помощи SRP или command buffer подхватить карту теней и найти в ней нужный фрагмент для оценки освещения, я ожидал прочитать об этом. В случае deferred режима рендеринга Light occlusion и lighting буферы можно оценить на этапе lighting pass.

Не подскажете, как?
Интересно. Я думал об этом способе, но не смог найти вменяемых реализаций. Надо будет перепроверить информацию и, если вы правы, дополнить статью.
Спасибо!
Способ конечно забавный, но неужели рендер и последующее чтение из текстуры получается быстрее и лучше чем простой рейкаст до источника? Получается 1 рейкаст до каждого источника, источников конечно может быть в сцене много, но не запредельное количество. 40 мс на 1 детектор выглядит как оверкилл, даже если делать это раз в секунду. Это же все равно скачок времени на рендер одного кадра получается. Рейкасты хотя бы можно раскидать по разным фреймам, чтобы сгладить просадку. Я юнити ковырял мало, но там разве над коллайдерами внутри нет какой-то ускоряющей структуры для рейкастов? Это же вроде бы не должно быть дорогой операцией.
Верно, поэтому у меня рейкастам посвящена половина статьи. Способ хороший, не спорю, но это потенциально десятки и десятки коллайдеров и рейкастов в каждой сцене. Плюс есть такие вещи, как запеченный свет и environment lightning. Там нельзя оценить свет одним рейкастом, требуется отдельная отладка.
Ну да, про переотраженный свет как-то не подумал. да и вопросы к протяженным источникам

Второй подход можно оптимизировать написав для сферы Шейдер(за основу можно взять пример diffuse lighting из документации юнити (https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html)). В frag методе посчитать средний цвет всех пикселей и присвоить результат переменной, например, float _illumination. Значение этой переменной можно достать в monobehaviour скрипте имея ссылку на материал с этим шейдером и зная имя переменной, используя метод Material.GetFloat("_illumination"). Таким образом вычисление освещенности ложится на видеокарту, а с этим она справляется хорошо, отпадает необходимость использовать медленный Render Texture, и получение значения освещенности можно делать хоть каждый кадр, без падения производительности.

Тоже интересный способ, спасибо, изучим!
Давно ли шейдер может писать обратно в переменные, которые ставятся по сути как стейты на цпу? Material.GetFloat вернет закешированное значение, установленное ранее через Material.SetFloat.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории