Уравнение рендеринга является одной из основ современной фотореалистичной графики. Оно встречается повсеместно, и если вы хотите отрендерить что-то с учётом освещения, то рано или поздно (скорее рано) на него наткнётесь. В этой статье я хочу подробно разобрать это уравнение, и как оно стыкуется с практическим применением.
Уравнение рендеринга: простое и интуитивно понятное. Но я пройдусь по каждой части, чтобы не было двусмысленностей.
- направление от камеры к точке рендеринга.
- исходящий свет в направление
.
- полусфера с "радиусом" 1 (что служит для упрощения формул, а не радиусом в мировых координатах), с центром вокруг нормали
, содержащая все направления, для которых
.
- интегрирование всей видимой полусферы над точкой. Если объект может пропускать через себя свет, то нужно интегрировать всю сферу.
- количество света поступающего из направления
.
- косинус угла между
и
- нормалью поверхности. Аналогично
, что более предпочтительно с точки зрения производительности. Чем больше угол, тем на большую площадь приходится одинаковое количество энергии.
- функция, определяющее количество света, которое может быть отражено из направления
в
. Также называется функцией материала. Обычно здесь находится Bidirectional reflectance distribution function, где Bidirectional означает, что
. Физически корректные формулы также должны отражать
количество энергии, получаемое из всей видимой полусферы. То есть, чем более гладкий материал, тем больше света из направления
отражаются в направление идеального (зеркального) отражения, и тем более яркое и маленькое будет пятно отражённого света, и тем темнее будет выглядеть остальная поверхность. Полностью не гладкий (диффузный, Ламбертовый) материал рассеивает свет одинаково во всех направлениях. В интернете существуют дата сеты с измеренными функциями BRDF реальных материалов.
Однако, в графике в реальном времени мы не хотим заниматься интегрированием. Оценивать количество света поступающее из бесконечного количества возможных направлений довольно дорого. Поэтому мы стараемся при оценке освещения избавиться от интеграла всеми возможными способами. Также мы делим уравнение рендеринга на части, по скольку эффективные алгоритмы расчёта света от разных источников разные.
Свет Солнца и неба
При оценке освещения от, например, Солнца мы можем допустить, что оно бесконечно далеко, и что для всех интересующих нас точек направление к Солнцу и его интенсивность является одинаковым. Тогда, оценивая освещение поверхности в вакууме, нас волнует только одно направление (Directinal Light) к свету. Если Солнц несколько, нужно просто просуммировать их влияние.
Говоря про вакуум, это довольно точная формула. Однако она полностью игнорирует тот факт, что на Земле атмосфера рассеивает свет по всему небосводу. В этом случае мы не можем просто отбросить интеграл:
Однако, если мы имеем дело с облачной погодой, то можно сделать допущение, что свет рассеивается примерно одинаково, и что количество света примерно одинаковое со всех направлений. Тогда весь интеграл заменяется на . Однако, простая константа не учитывает перекрытие этого света другими объектами. Для того, чтобы сгладить неточность первого допущения, мы вводим второе в виде Ambient Occlusion, которое обычно вычитается из общего света.
Однако, что если наш искомый объект очень гладкий, и он находится на Земле среди других объектов? Показать зеркальный объект без отражений нельзя. У нас не остаётся другого выбора, кроме как просуммировать влияние отражённого света от всех окружающих вещей:
Мы разделяем прямое влияние источников света (что довольно просто посчитать), и влияние непрямое. В более привычном виде: . Только недавно, с ростом "грубой силы" видеокарт, мы начали пытаться рассчитать непрямое влияние в реальном времени. С зеркалами всё относительно просто, по скольку нам нужно посылать луч только в одном направление. А вот честное интегрирование диффузных материалов остаётся непосильной задачей, по скольку в среднем у нас есть бюджет на от 1 до 4 лучей на пиксель (зачастую, менее одного!). Приходится идти на огромное количество ухищрений, чтобы получить правдоподобный результат: расчёт с пониженным разрешением, применение денойзеров для борьбы с неизбежным шумом при таком количестве лучей, а также временное накопление результатов.
Свет из точки
Здесь мы начнём глубже знакомиться с . Для Directional light всё просто, свет равен единице в одном направление, и равен нулю во всех других. Однако, для точечного источника света нам теперь важна позиция.
Где:
- интенсивность света.
- точка, для которой рассчитывается освещение.
- функция затухания света.
- функция видимости, равна 1 - shadow.
Для точечного источника света, есть простая формула для функции затухания:
, где
это расстояние от источника света до
. Это очень простая и физически корректная формула. Однако, здесь есть одна практическая проблема: когда
. Когда мы строим сцену с реалистичным освещением, мы пытаемся с помощью точечных источников света приблизительно отразить влияние реальных источников света, у которых есть какой-то размер. И этот нюанс с бесконечностью довольно сильно мешает. Типичными костылями являются:
и
, где
является арбитрарно взятым числом. Не буду на них подробно останавливаться, и так очевидна костыльность.
Однако, а что если добавить радиус к источнику света? Да, точка с радиусом звучит странно, для кого-то очень странно, однако этот радиус мы будем использовать только для функции затухания, функция материала всё ещё будет рассчитывать свет только из одного направления. Это гораздо более элегантное решение проблемы, баланс реалистичности и производительности.
Пример такой функции: , однако она затухает медленнее, чем физически корректная.
Физически корректная функция для точечного источника света с радиусом выглядит так:
, подробнее о выведение см тут.
У этой формулы есть несколько преимуществ: 1) интенсивность света достигает константы при нулевом расстояние, 2) при большом расстояние значения приближаются к стандартной формуле, 3) при нулевом радиусе работает точно также, как и стандартная формула.
Сравнение функций затухания

Под ad hoc имеется в виду
Сравнение функций на сцене с источником света одинаковой яркости:




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

Каждое ребре треугольника определяет сектор диска ("срез пиццы"), заданный углом
и нормалью
.
Площадь сектора диска пропорциональна его углу:

Знаковая проецируемая площадь сектора диска равна его площади , умноженной на знаковый косинус его нормали
. Разделив знаковую проецируемую площадь на
, получим знаковую освещённость сектора диска:
В итоге, мы получаем общее освещение треугольника как сумму знаковых освещённостей секторов диска:
Все эти формулы были выведены Ламбертом ещё в 1760 году. В итоге мы можем рассчитать прекрасное диффузное освещение:

Однако, здесь есть одна практическая проблема. Так мы можем рассчитать только диффузное освещение, а нам бы хотелось иметь возможность применить это освещение к любой поверхности.
К счастью, в 2016 появилась работа, которая описывает использование нового семейства распределений Линейно Трансформированных Косинусов (LTCs). LTC позволяют с достаточной точностью описывать влияние полигонального освещения на PBR поверхность. Всё что для этого нужно: LUT текстура с заранее проинтегрированными параметрами, а также рассчитанное нами диффузное освещение.


Это позволяет нам в реальном времени рассчитать влияние любого полигонального источника света на практически любую поверхность. Стоимость растёт линейно в зависимости от количества рёбер + стоимость чтения из LUT текстуры.

Как вишенка на торте, мы также можем использовать текстуру на источнике света, и семплировать разные уровни Lod для имитации рассеивания из-за расстояния и шероховатости!

Остаётся лишь один нюанс: мы не знаем как аналитически рассчитать мягкие тени от полигонального источника света. Стандартные карты теней не могут отразить объёмность источника света, а приблизительные методы делают это довольно плохо. Судя по всему, быстрее рейтрейсинг войдёт в обиход, чем появится такой метод. Остаётся только использовать жёсткие тени с PCF, и надеяться, что люди не будут заострять на этом внимание.
Спасибо за прочтение до конца. Ссылка на имплементацию шейдера с LTC. Ссылка на пример генерации Lut текстур.
MasterMentor
Тот случай, когда статья вызывает Wow! эффект (ИМХО).