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

Загрузка сцены

Начнем с окружающей среды или «сцены». Обычно это передается в программу как набор трехмерных точек (вершин), каждая из которых описывает точку на поверхности объекта. За ним следует список граней, соединяющих вышеупомянутые вершины в 2D-формы, которые в конечном итоге создают сцену, за которой следует наблюдать. Мы называем эти формы «примитивами», поскольку они являются простейшими частями зачастую сложной структуры. Сцены часто встречаются в форматах .obj или .ply, но в отрасли отсутствует универсальный формат файлов, и обработка как можно большего числа файлов для создания надежного синтаксического анализатора является частью создания хорошего трассировщика лучей.

Делаем это быстро

После того, как мы загрузили положение и ориентацию каждого примитива в сцене, мы переходим ко второму шагу, иерархии ограничивающего объема (BVH). BVH, хотя и не является обязательным для программы для рендеринга изображения, является фундаментальным аспектом ускорения любого приличного трассировщика лучей. Основная причина, по которой трассировка лучей и, в частности, трассировка пути медленнее, чем другие алгоритмы рендеринга, - это коллизия. Чтобы луч мог определить, что он столкнулся с примитивом, он должен явно проверить этот примитив. Это означает, что каждый отбрасываемый луч должен проверять каждый примитив в сцене, чтобы точно знать, какой из них ближайший. При рассмотрении сложных сцен, построенных из миллионов примитивов, легко увидеть, как это может быстро стать узким местом.

То, что делает BVH, по существу устраняет возможность столкновения лучей, поэтому они не делают дорогостоящих вычислений для столкновения с поверхностями, которые не имеют отношения к делу. Окружение по существу разрезано пополам, так что половина примитивов находится с одной стороны, а половина - с другой. Когда направляются лучи, они сначала спрашивают, в какой половине окружения они находятся, поэтому им нужно только проверить столкновение примитивов, находящихся в пределах их половины, эффективно устраняя 50% столкновений. Это делается не только один раз, но и несколько раз, пока сцена не будет разбита на удобоваримые куски. На каждом уровне луч может устранить половину своей нагрузки от столкновений, сначала спросив, какую половину объема он занимает. Этот процесс будет подробно рассмотрен в отдельной статье, так что не беспокойтесь, если сразу все не будет понятно на 100%.

Отливка лучей!

Теперь, когда сцена была тщательно изучена, мы можем перейти к интересным делам. Лучи!

На самом деле свет работает просто. Источник света излучает лучи света, которые отражаются и преломляются по всему миру, при этом часть энергии и длины волны поглощаются по пути, пока не достигнут наблюдателя. Однако такое вычисление света было бы неэффективным, поскольку подавляющее большинство лучей не доходило до наблюдателя. Трассировщик лучей делает наоборот. Мы направляем лучи от наблюдателя, «прослеживая» пути лучей до их источников света. Таким образом, мы учитываем только лучи света, которые достигают наблюдателя.

Это точка, в которой трассировка пути начинает отличаться от стандартной трассировки лучей. Чтобы создать изображение с помощью трассировки лучей, луч создается для каждого пикселя изображения. Представьте, что все лучи начинаются с одной и той же точки на камере и проходят через экран в окружающую среду. Каждый луч будет направлен по траектории, где он будет пытаться «найти свет». Когда луч сталкивается с поверхностью, он отбрасывает луч из этой точки на источники света в сцене, чтобы проверить, освещается ли поверхность напрямую или находится в тени. Мы называем их «умными лучами», поскольку каждый луч знает, где находятся источники света, и пытается визуализировать изображение, специально сверяя их с ними.

Трассировка пути вместо использования нескольких из этих умных лучей для рендеринга изображения использует много «немых лучей». В отличие от выборки каждого пикселя одним лучом и возврата цвета, трассировка пути производит выборку каждого пикселя несколько раз случайным образом и усредняет результат всех лучей, прошедших через пиксель. Некоторые из этих лучей будут отражаться и попадать в источник света, а некоторые - нет. Чем больше мы сэмплируем каждый пиксель, тем точнее будет цвет пикселя для представления того, сколько света он получил.

Этот процесс называется интеграцией Монте-Карло. Чем больше лучей мы излучаем, тем точнее будет физически изображение. Хорошей аналогией была бы перепись среднего роста мужчины в Соединенных Штатах. Если бы вы взяли 10 случайных мужчин и усреднили их рост, ваш результат мог бы быть искажен. Но если бы вы усреднили рост каждого мужчины в США, вы бы получили совершенно точный результат. Усреднение каждого луча света, конечно, невыполнимая задача, учитывая, что существует бесконечное количество лучей света, поэтому мы используем случайность для представления бесконечной совокупности лучей.

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