Урок №43. IBL. Зеркальная облученность

  Дмитрий Бушуев  | 

  Обновл. 16 Дек 2020  | 

 1106

На предыдущем уроке мы настроили работу PBR в сочетании с освещением на основе изображений (IBL), предварительно рассчитав для карты освещенности диффузную часть непрямого освещения. На этом уроке мы сосредоточимся на зеркальной части уравнения отражения.

Зеркальная часть уравнения отражения

Вспомним наше уравнение отражения:

Вы увидите, что зеркальная часть Кука-Торренса (слагаемое в скобках, которое умножается на kS) не является для интеграла константой и зависит не только от направления входящего света, но и от направления (входящего) обзора. Попытка решить интеграл для всех входящих световых направлений, включая всевозможные направления обзора, приведет к слишком большим затратам ресурсов компьютера и времени, особенно учитывая то, что этот расчет должен проводиться в режиме реального времени. Компания Epic Games предложила решение — методику под названием аппроксимация разделенной суммы, которая позволяет предварительно провести (правда с некоторыми оговорками) свертку зеркальной части уравнения.

Аппроксимация разделенной суммы разделяет зеркальную часть уравнения отражения на две отдельные части, к которым мы можем по отдельности применить операцию свертки и позже объединить их в PBR-шейдере для реализации зеркального непрямого освещения на основе изображений. Подобно тому, как мы предварительно свернули карту облученности, аппроксимация разделенной суммы требует передать ей HDR-карту окружения в качестве входных данных свертки. Чтобы понять алгоритм аппроксимации расщепленной суммы, мы снова рассмотрим уравнение отражения, но на этот раз сосредоточимся на его зеркальной части:

По тем же причинам (производительности), как и в случае с операцией свертки облученности, мы не можем в режиме реального времени рассчитать зеркальную часть интеграла и надеяться при этом на разумную производительность. Поэтому рациональнее будет сначала предварительно вычислить этот интеграл для получения чего-то вроде зеркальной IBL-карты, а затем при помощи нормали фрагмента произвести выборку из этой карты. Однако именно здесь всё становится немного сложнее. На предыдущем уроке мы смогли предварительно вычислить карту облученности благодаря тому, что интеграл зависел только от ωi, и у нас была возможность вынести постоянные члены диффузного альбедо из-под знака интеграла. На этот раз, как видно из BRDF, интеграл зависит не только от ωi:

Он также зависит и от ω0, поэтому мы не можем производить выборки из предварительно вычисленной кубической карты. Положение точки p здесь не играет никакой роли. Попытка выполнить предварительные вычисления данного интеграла для каждой возможной комбинации ωi и ω0 в режиме реального времени — это, мягко говоря, непрактичный подход.

Идея аппроксимации разделенной суммы, выдвинутая компанией Epic Games, решает эту проблему, разбивая предварительное вычисление на 2 отдельные части, которые мы позже можем объединить для получения результирующего предварительно вычисленного результата, который нам нужен. Аппроксимация разделенной суммы разбивает интеграл зеркальной составляющей на два отдельных интеграла:

Свертка первой части известна как предварительно отфильтрованная карта окружения, которая (аналогично карте облученности) является предварительно вычисленной картой свертки окружения, но на этот раз с учетом шероховатости поверхности. Для увеличения уровня шероховатости карта окружения свернута с рассредоточенными векторами выборки, создавая более размытые отражения. Результаты последовательного размытия для каждого уровня шероховатости, который мы подвергаем свертке, сохраняются в предварительно отфильтрованных мипмап-уровнях карты. Например, предварительно отфильтрованная карта окружения, хранящая предварительно рассчитанный результат свертки 5 различных значений шероховатости для 5 своих мипмап-уровней, выглядит следующим образом:

Далее, мы генерируем векторы выборки и величину их разброса, используя функцию нормального распределения (NDF) BRDF Кука-Торренса, которая принимает в качестве входных данных вектор нормали и вектор направления обзора. Поскольку при свертке карты окружения направление обзора заранее не известно, то по методу Epic Games последующая аппроксимация выполняется, предполагая, что направление обзора (и, следовательно, направление зеркального отражения) совпадает с исходящим направлением выборки ω0. Это приводит к следующему коду:

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

Вторая часть уравнения разделенной суммы эквивалентна BRDF-части зеркального интеграла. Если мы представим, что входящая энергетическая яркость для всех направлений представлена полностью белым светом (L(p, x) = 1.0), то мы можем предварительно рассчитать значение BRDF с учетом параметра шероховатости и угла между нормалью n и направлением света ωi (или их скалярного произведения n ⋅ ωi). По методу Epic Games, предварительно вычисленный результат BRDF для каждой комбинации вектора нормали и направления света при различных значениях шероховатости сохраняется в виде 2D LUT-текстуры (сокр. «LUT» от англ. «Look Up Table» = «Таблица поиска»), известной как карта интегрирования BRDF. 2D LUT-текстура отображает масштаб (красный) и значение смещения (зеленый) для расчета коэффициента Френеля поверхности, в результате чего мы получаем решение для второй части разделенного интеграла:

Мы генерируем LUT-текстуру путем обработки горизонтальной координаты текстуры (в диапазоне [0.0, 1.0]) в качестве входного значения n ⋅ ωi BRDF, а вертикальной координаты — в качестве входного значения шероховатости. Объединяя данную карту интегрирования BRDF и предварительно отфильтрованную карту окружения, мы получаем значение зеркального интеграла:

Эта теория даст нам немного общего представления о том, каким образом при помощи аппроксимации расщепленной суммы (по методу Epic Games) реализуется приближение значения зеркальной части непрямого освещения уравнения отражения.

Предварительная фильтрация HDR-карты окружения


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

Во-первых, нам нужно создать новую кубическую карту для хранения данных предварительно отфильтрованной карты окружения. Используя функцию glGenerateMipmap() можно быть уверенными, что у нас будет достаточный объем памяти для мипмап-уровней карты окружения:

Обратите внимание, что поскольку мы планируем производить выборку из мипмап-карт переменной prefilterMap, то нам нужно будет убедиться, что фильтр уменьшения задан как GL_LINEAR_MIPMAP_LINEAR, благодаря чему задействуется трилинейная фильтрация. Мы храним предварительно отфильтрованные зеркальные отражения в разрешении 128×128 (каждой грани) базового мипмап-уровня. Этого будет достаточно для большинства материалов и их отражений, но если у вас есть большое количество гладких материалов (например, автомобильные отражения), то вы можете увеличить разрешение.

На предыдущем уроке мы свернули карту окружения при помощи сферических координат, генерируя векторы выборки, равномерно распределенные по полусфере Ω. Хотя это и прекрасно работает для облучения, но для зеркальных отражений данный подход менее эффективен. Когда речь заходит о зеркальных отражениях, зависящих от шероховатости поверхности, то важно понимать одну деталь, а именно: отражения света могут совпадать с вектором отражения r поверхности или быть (если поверхность не очень шероховата) разбросанными относительно вектора отражения:

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

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

Интегрирование по методу Монте Карло и выборка по значимости

Чтобы полностью понять важность процесса выборки по значимости, мы сначала углубимся в математическую конструкцию, известную как интегрирование по методу Монте-Карло. Интегрирование по методу Монте-Карло тесно связано с такими математическими дисциплинами, как статистика и теория вероятностей. Метод Монте-Карло помогает нам дискретно решить задачу вычисления некоторой статистики или значения популяции, не принимая во внимание всей популяции.

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

Другой подход состоит в том, чтобы провести измерения на небольшом подмножестве людей, элементами которого являются выбранные случайным образом (несмещенные) элементы исходного множества (всей популяции), измерить их рост и усреднить результат. Причем в этой популяции может быть всего лишь 100 человек. Хотя полученный результат будет не так точен, как при прямом подходе, но вы получите близкий к истине ответ. Описанный метод подсчета известен как закон больших чисел. Его идея заключается в том, что если вы измеряете некоторое меньшее подмножество (размера N) случайных выборок из общей совокупности, то результат будет приближен с некоторой погрешностью к истинному ответу и становится еще ближе к нему по мере увеличения числа выборок N.

Интегрирование по методу Монте-Карло основывается на законе больших чисел и использует тот же принцип при решении интеграла. Вместо того, чтобы решать интеграл для всех возможных (теоретически, их может быть бесконечно много) значений (выборки) x, просто сгенерируйте N значений выборки, случайным образом отобранных из общей совокупности и усредните их. По мере увеличения N мы будем получать результат, приближающийся к точному ответу решения интеграла:

Чтобы решить интеграл, мы берем N случайных выборок в интервале от a до b, складываем их вместе и делим на общее число выборок, усредняя тем самым их сумму. pdf — это функция плотности вероятности, которая сообщает нам о вероятности появления конкретной выборки в общем наборе выборок. Например, данная pdf функция роста людей некоторой популяции будет выглядеть примерно так:

Как видно из графика, если мы возьмем любой случайный элемент (человека) из нашего совокупного множества (населения), то вероятность, что выбранный человек будет иметь рост 1.70 — больше, по сравнению с вероятностью того, что выбранный человек будет иметь рост 1.50.

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

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

Интегрирование по методу Монте-Карло довольно распространено в компьютерной графике, поскольку является интуитивно понятным и достаточно эффективным способом дискретной аппроксимации непрерывных интегралов: возьмите любую область/объем (например, полусферу Ω), сгенерируйте N случайных выборок в пределах этой области/объема и просуммируйте их, взвешивая вклад каждой выборки в конечный результат.

Интегрирование по методу Монте-Карло — это большой раздел математики, и я не хочу углубляться в его подробности. Упомяну только один момент — существует несколько способов генерации случайных выборок. По умолчанию, каждая выборка полностью (псевдо)случайна, как мы и привыкли, но, используя определенные свойства квазислучайных последовательностей, мы можем генерировать векторы выборки, которые все еще являются случайными, но при этом имеют интересные свойства. Например, мы можем выполнить интегрирование по методу Монте-Карло на так называемых последовательностях с низким расхождением, которые все еще генерируют случайные значения выборки, но каждое значение распределяется более равномерно:

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

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

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

Последовательность с низким расхождением

На данном уроке мы произведем предварительный расчет зеркальной части уравнения непрямого отражения, используя метод интегрирования квази Монте-Карло и выборку по значимости для случайной последовательности с низким расхождением. Последовательность, которую мы будем использовать, известна как последовательность Хэммерсли, подробно описанная Holger Dammertz. В основе последовательности Хэммерсли лежит последовательность Ван дер Корпуса, которая отзеркаливает двоичное представление десятичной дроби относительно её десятичной точки.

Учитывая некоторые хитрые побитовые трюки, мы можем довольно эффективно генерировать последовательность Ван дер Корпуса в шейдерной программе, которую используем для получения i-го элемента последовательности Хэммерсли по N общим выборкам:

GLSL-функция Hammersley() дает нам i-й элемент (последовательности с низким расхождением) из общего множества (размера N) выборок.

Последовательность Хэммерсли без поддержки побитовых операторов

Не все OpenGL-драйверы поддерживают побитовые операторы (например, WebGL и OpenGL ES 2.0), и в этом случае вы можете использовать альтернативную версию последовательности Ван дер Корпуса, которая не зависит от побитовых операторов:

Обратите внимание, что из-за некоторых ограничений, которые GLSL накладывает на циклы, на более старом оборудовании цикл может проходиться по всем 32 битам. Эта версия менее производительна, но работает на всех аппаратных средствах, где не поддерживаются побитовые операторы.

Выборка по значимости и GGX

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

Кроме того, чтобы построить вектор выборки, нам нужно будет каким-то образом сориентировать и сместить вектор выборки в сторону зеркального лепестка поверхности, определяемого некоторым значением шероховатости поверхности. Мы можем взять NDF и объединить GGX NDF c процессом выборки векторов с помощью сферических координат, как это предлагает делать Epic Games:

Благодаря этому, мы получаем вектор выборки, в некоторой степени ориентированный вдоль предполагаемого срединного вектора микроповерхности, зависящего от некоторого параметра шероховатости поверхности и значения элемента Xi последовательности с низким расхождением. Обратите внимание, что компания Epic Games, используя исследования компании Disney в области теории PBR, для улучшения визуальных результатов применяет квадрат шероховатости.

Определив последовательность Хэммерсли с низким расхождением и генерацию векторов выборки, мы можем доработать шейдер предварительного фильтра и свертки:

Мы предварительно фильтруем карту окружения, в зависимости от некоторого входного значения шероховатости поверхности, которое меняется (в диапазоне [0.0, 1.0]) с каждым мипмап-уровнем предварительной отфильтрованной кубической карты, и сохраняем результат в переменной prefilteredColor. Полученный предварительно отфильтрованный цвет делится на совокупный вес всех значений выборки, в которой значения выборки с меньшим влиянием на конечный результат (для малых значений NdotL) вносят меньший вклад в конечный вес.

Фиксирование предварительно отфильтрованных мипмап-уровней

Что остается сделать, так это позволить OpenGL предварительно отфильтровать карту окружения с различными значениями шероховатости на нескольких мипмап-уровнях. На самом деле это довольно легко сделать:

Данный процесс аналогичен процессу свертки карты облученности, но на этот раз мы масштабируем размеры фреймбуфера под соответствующий масштаб мипмап-карты, при этом размеры каждого мипмап-уровня уменьшаются в 2 раза. Кроме того, в последнем параметре функции glFramebufferTexture2D() мы указываем мипмап-уровень, в который собираемся выполнять рендеринг, и передаем параметр шероховатости, используемый при предварительной фильтрации, в шейдер предварительного фильтра.

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

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

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

Артефакты свертки предварительной фильтрации

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

Появление швов в кубической карте

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

К счастью для нас, в OpenGL встроена возможность правильно отфильтровать грани кубической карты, включив режим GL_TEXTURE_CUBE_MAP_SEAMLESS:

Просто включите это свойство где-нибудь в начале вашего приложения, и швы исчезнут.

Яркие точки

Из-за часто повторяющихся деталей и широко изменяющейся интенсивности света в зеркальных отражениях, свертка зеркальных отражений требует большого количества выборок, чтобы должным образом учесть широко изменяющуюся яркость отражений HDR-окружения. Мы уже взяли довольно большое количество выборок, но в некоторых средах этого все еще может быть недостаточно, и в этом случае вы начнете видеть точки, появляющиеся вокруг ярких областей:

Одним из вариантов решения данной проблемы является дальнейшее увеличение количества выборок, но этого будет недостаточно для всех возможных вариантов окружения. Как описал Chetan Jags, мы можем уменьшить этот артефакт производя (во время свертки предварительного фильтра) не прямую выборку значений из окружения, а выполняя выборку из мипмап-уровня карты окружения с использованием функции плотности вероятности интеграла и параметра шероховатости:

Не забудьте включить трилинейную фильтрацию для карты окружения, из мипмап-уровней которой вы собираетесь производить выборку значений:

И теперь пусть OpenGL сгенерирует мипмап-карты после того, как будет установлена базовая текстура кубической карты:

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

Предварительное вычисление BRDF


Теперь, имея предварительно отфильтрованную карту окружения, мы можем сосредоточиться на второй части аппроксимации разделенной суммы, а именно — на BRDF. Давайте еще раз кратко рассмотрим аппроксимацию разделенной суммы:

Мы предварительно вычислили левую часть разделенной суммы на этапе предварительного фильтра карты для различных уровней шероховатости. Правая сторона требует, чтобы мы свернули уравнение BRDF, используя угол n ⋅ ωo, значение шероховатости поверхности и коэффициент Френеля F0. Это похоже на интегрирование зеркальной BRDF с окружением, имеющим сплошной белый свет или с постоянным значением энергетической яркости Li = 1.0. Свертка BRDF по 3 переменным является сложной задачей, но мы можем попытаться вынести F0 из уравнения зеркальной BRDF:

Функция F — это вычисление коэффициента Френеля. Перенося знаменатель BRDF, мы получаем следующее эквивалентное уравнение:

Выполняя подстановку аппроксимации Френеля-Шлика вместо крайней правой F, мы получаем:

Заменим (1 − ωo ⋅ h)5 на α, чтобы упростить решение для F0:

Затем мы представим функцию Френеля F в виде суммы двух интегралов:

Таким образом, F0 является для интеграла константой, и мы можем вынести её из-под знака интеграла. Затем мы производим обратную замену для α, получая тем самым окончательное уравнение BRDF с разделенной суммой:

Два результирующих интеграла представляют собой масштаб и смещение для коэффициента F0. Обратите внимание, что поскольку fr(p, ωi, ωo) уже содержит в себе F, то они оба сокращаются и исчезают из fr.

В похожей манере, как с ранее свернутыми картами окружения, мы можем свернуть уравнения BRDF с их входными данными, а именно: углом между n и ωo и параметром шероховатости. Результаты свертки сохраним в 2D LUT-текстуре, известной как карта интегрирования BRDF, которую позже используем в нашем PBR-шейдере освещения, чтобы получить окончательный результат непрямого зеркального освещения.

Шейдер свертки BRDF работает на плоскости, непосредственно используя свои 2D текстурные координаты в качестве входных данных для свертки BRDF (NdotV и roughness). Код свертки во многом аналогичен свертке предварительного фильтра, за исключением того, что теперь он обрабатывает вектор выборки в соответствии с геометрической функцией нашей BRDF и приближением Френеля-Шлика:

Как вы можете видеть, свертка BRDF — это прямой перенос математических выкладок непосредственно в код. В качестве входных данных мы берем угол θ и параметр шероховатости, при помощи метода выборки по значимости генерируем вектор выборки, обрабатываем его с использованием геометрической функции и полученного уравнения Френеля для BRDF и выводим как величину масштабирования, так и величину смещения для F0 для каждой выборки, усредняя их в конце.

Геометрический член BRDF имеет небольшое отличие при использовании вместе с IBL, поскольку его переменная k имеет несколько иную интерпретацию, а именно:

Поскольку свертка BRDF является частью зеркального IBL интеграла, то мы будем использовать kIBL для геометрической функции модели Шлика-GGX:

Обратите внимание, что в то время, когда k принимает переменную a в качестве параметра, мы не возводим в квадрат переменную roughness (как и переменную a), как мы изначально делали это для других случаев интерпретации a; скорее всего, a здесь уже в квадрате. Я не уверен, является ли это несоответствием со стороны Epic Games или оригинальной диснеевской работы, но прямой перевод переменной roughness в переменную a дает карту интегрирования BRDF идентичной, которая описывалась в версии от Epic Games.

Наконец, чтобы сохранить результат свертки BRDF, мы создадим 2D-текстуру с разрешением 512×512:

Обратите внимание, что мы используем 16-битный формат данных типа с плавающей точкой, рекомендованный Epic Games. Обязательно задайте режим наложения как GL_CLAMP_TO_EDGE, чтобы предотвратить появление артефактов у краев.

Затем мы повторно используем тот же объект фреймбуфера и запускаем шейдер применительно к NDC прямоугольника экранного пространства:

Результат свертки BRDF-части разделенной суммы должен дать вам следующее изображение:

Как с помощью предварительно отфильтрованной карты окружения, так и с помощью BRDF 2D LUT-текстуры мы можем восстановить интеграл непрямого зеркального освещения в соответствии с аппроксимацией разделенной суммы. Затем совмещенный результат используется в качестве непрямого света или отраженного света фонового освещения.

Завершение вычисления отражения при использовании IBL

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

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

На шаге предварительной фильтрации мы свернули карту окружения для максимума из 5 мипмап-уровней (от 0 до 4), который мы задаем при помощи MAX_REFLECTION_LOD, чтобы гарантировать, что мы не задействуем мипмап-уровень, в котором нет никаких (релевантных) данных.

Затем, учитывая шероховатость материала и угол между нормалью и вектором вида, производим выборку значения из LUT-текстуры BRDF:

Полученные из LUT-текстуры BRDF значения масштабирования и смещения к F0 (здесь мы непосредственно используем коэффициент Френеля F) объединяются с предварительным фильтром левой части уравнения отражения IBL и значение аппроксимируемого интеграла восстанавливается в переменной specular.

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

Обратите внимание, что мы не умножаем переменную specular на kS, так как в ней уже содержится коэффициент Френеля.

Теперь, запустив наш код на серии сфер, которые отличаются своей шероховатостью и металлическими свойствами, мы наконец-то увидим их истинные цвета в окончательном PBR-рендере:

  GitHub / Урок №43. IBL. Зеркальная облученность — Исходный код №1

Мы могли бы пойти еще дальше и использовать некоторые классные текстурированные PBR-материалы:

  GitHub / Урок №43. IBL. Зеркальная облученность — Исходный код №2

Или загрузить эту удивительную бесплатную 3D PBR-модель от Андрея Максимова:

Наше освещение теперь выглядит гораздо более убедительным. А что еще круче, так это то, что наше освещение выглядит физически корректным независимо от того, какую карту окружения мы используем. Ниже вы увидите несколько различных предварительно вычисленных HDR-карт, полностью меняющих динамику освещения, но все еще выглядящих физически корректными при том, что ни одна переменная освещения не была изменена!


Что дальше?


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

По этой причине обычно карта окружения предварительно вычисляется только один раз, а затем сохраняется на диске (обратите внимание, что карта интегрирования BRDF не зависит от карты окружения, поэтому вам нужно только вычислить или загрузить её один раз). Это означает, что вам нужно будет придумать собственный формат изображения для хранения кубических HDR-карт, включая их мипмап-уровни. Или же вы сохраните (и загрузите) её в одном из доступных форматов (например, в формате .dds, поддерживающем хранение мипмап-уровней).

Кроме того, в наших руководствах мы описали весь процесс, включая генерацию предварительно вычисленных изображений IBL и подготовки PBR-конвейера. Но вы с тем же успехом можете использовать несколько замечательных инструментов таких, как cmftStudio или IBLBaker, чтобы создать предварительно вычисленные карты.

Один момент, который мы пропустили — это предварительно вычисленные кубические карты в качестве зондов отражения: интерполяция кубических карт и коррекция параллакса. Это процесс размещения нескольких зондов отражения в вашей сцене, которые делают снимок кубической карты сцены в данном конкретном месте, который мы затем можем свернуть в виде данных IBL для этой части сцены. Путем интерполяции между несколькими из этих зондов на основе близости камеры мы можем достичь локального освещения на основе изображений с высокой детализацией, ограниченное только лишь количеством зондов отражения, которые мы готовы разместить. Таким образом, освещение на основе изображения может корректно обновляться при переходе от яркой наружной части сцены к более темной внутренней части.

Дополнительные ресурсы

   Real Shading in Unreal Engine 4: объясняется метод аппроксимации разделенной суммы, предложенный компанией Epic Games.

   Physically Based Shading and Image Based Lighting: отличный пост в блоге Трента Рида об интеграции зеркального IBL в PBR-конвейер в режиме реального времени.

   Implementation Notes: Runtime Environment Map Filtering for Image Based Lighting: обширная запись Падраика Хеннесси о предварительной фильтрации HDR-карт окружения и значительной оптимизации процесса выборки.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (4 оценок, среднее: 5,00 из 5)
Загрузка...

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *