Pull to refresh

Простой шейдер для точечных источников света в тумане

Reading time 3 min
Views 4.6K
Original author: Isaac Dykeman
Мне нужен был простой и быстрый шейдер, создающий туман, освещённый точечными источниками света. Для его реализации я написал эффект экранного пространства, результаты работы которого показаны ниже. Конвейер почти столь же прост, что и для обычных точечных источников. Он не требует структур данных объёмов, ray marching, и может быть без проблем подключен к уже существующему шейдеру освещения.

Важнейший принцип заключается в том, что можно вычислить в замкнутой форме свет, исходящий из тумана, как будто он освещён точечным источником освещения. Моё решение заключалось в поиске формулы и её подстановке в шейдер.


Небольшая сцена с космическим кораблём, отрендеренная в тумане при помощи моей техники

The basic setup

Модель тумана


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

  • Шейдер предполагает, что туман рассеивает освещение равномерно во всех направлениях. Реальный туман или дым не всегда обладают таким свойством, но это вполне хорошая аппроксимация для той картинки, к которой мы стремимся.
  • Шейдер предполагает, что все длины волн освещения взаимодействуют с туманом одинаково. В реальности это часто не так. Например, рэлеевское рассеяние делает небо голубым, рассеивая голубое освещение сильнее, чем другие длины волн.
  • Свет, исходящий из тумана, изменяется в зависимости от обратного квадрата расстояния от источника освещения до тумана. В реальности такого не бывает, но выглядит это нормально. Существует множество ресурсов, в которых объясняется, как туман на самом деле рассеивает освещение, и я уверен, что можно дополнить мою методику этими формулами.

Тем не менее. результаты выглядят правдоподобными даже при использовании этих упрощений.

Аналитическое решение


Пусть имеется бесконечно малый фрагмент тумана (представьте небольшой куб из тумана). Тогда свет, испускаемый этим фрагментом тумана в моей системе, будет иметь вид:

$\text{light} = \frac{1}{(\text{distance from light to fog fragment})^2}$


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

Для удобства перепишем уравнение:

$d = \text{distance from light to fog fragment}$


$\text{light} = \frac{1}{d^2}$


$\int_{\text{view line}} \text{light at} \hspace{1ex} x \hspace{1em} dx$


Или, если более формально:

$L = \text{light position}$


$w = \text{world fragment position}$


$c = \text{camera position}$


$\text{light arriving at camera} = \int_{c}^{w} \frac{1}{|x - L|_2} dx$



Этот интеграл выражает именно то, что нам нужно, но его вычисление сложнее, чем нам требуется. Так как все переменные на показанном выше изображении являются векторами с тремя значениями, нам, по сути, придётся иметь дело с 12 переменными. Я устраню большинство этих переменных, выполнив простую репараметризацию.

Зададим новое пространство под названием «пространство освещения» (light space)

  • По оси X откладывается линия взгляда
  • По оси Y откладывается освещение


Теперь вместо линейного интеграла в трёхмерном пространстве я просто выполняю интегрирование по оси X из камеры до фрагмента мира. Далее нам нужно переписать интеграл. Расстояние от точки $x$ на оси X до источника освещения равно $\sqrt{h^2+x^2}$. Поэтому интеграл по линии взгляда получает вид

$\int \frac{1}{h^2+x^2} dx$


Решив его вручную или в любимой системе компьютерной алгебры, получим:

$\int \frac{1}{h^2+x^2} dx = \frac{tan^{-1}(\frac{x}{h})}{h}$


То есть для получения освещения, исходящего из тумана для заданной линии взгляда я вычисляю этот интеграл от камеры до фрагмента мира. Если камера находится в $x=a$, а фрагмент мира в $x=b$, то освещение, поступающее в пиксель от освещённого тумана вдоль луча видимости, имеет вид:

$\int_a^b \frac{1}{h^2+x^2} dx = \frac{tan^{-1}(\frac{b}{h})}{h} - \frac{tan^{-1}(\frac{a}{h})}{h}$


Мысли о реализации


Мне удалось без серьёзных модификаций реализовать этот шейдер в моём конвейере отложенного затенения. Для этого понадобилось добавить порядка десяти строк кода на GLSL. Если конвейер уже вычисляет рассеянное + зеркальное отсвещение с точечными источнками света, то шейдер точечных источников и так должен иметь доступ к позиции камеры. источника освещения и текущего пикселя в мире, а это всё, что вам нужно!

Обработка нескольких источников освещения выполняется очень просто. Достаточно просто суммировать влияние рассеяния света туманом для каждого источника. Чтобы система была эффективной для множества источников, необходимо вычислять интеграл только для пикселей, настолько близких к источнику света, что он может внести свой вклад в заметное визуально количество света в сцене. Например, в конвейере отложенного затенения можно рендерить вокруг источника освещения сферический меш, чтобы шейдер вычислял воздействие освещения только для пикселей рядом с этим источником.

Я выяснил, что самой сложной частью реализации этого шейдера является правильное расположение камеры и фрагмента мира в пространстве освещения, чтобы мы могли воспользоваться упрощённым интегралом.

Результаты



Небольшой уровень, построенный алгоритмом Generate Worlds, с небольшим освещением тумана


Если источников больше, эффект становится сильнее


Если ещё и отслеживать информацию о направлении источников освещения, то можно создавать в тумане красивое фигурное освещение

Дополнительное чтение


Introduction to Light Scattering: An Imaging Sciences Perspective

A Practical Analytic Single Scattering Model for Real Time Rendering

Atmospheric scattering and volumetric fog algorithm part 1
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+11
Comments 3
Comments Comments 3

Articles