Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Форум / Phong BRDF, сходимость на граничных случаях

Phong BRDF, сходимость на граничных случаях

SuslikМодераторwww16 мая 201815:06#0
Рассмотрим BRDF фонга в одной из классических(лол) форм. Для ясности с умножением на косинус я оставляю как диффузный, так и спекулярный член, хотя с диффузом в этом случае всё понятно:
[cht]BRDF(\vec{w_i}, \vec{w_o}, \vec{n}) = k_d \frac{1}{\pi} + k_s  \frac{e+2}{2\pi} (\vec{w_o} \cdot (\vec{w_i} - 2\vec n (\vec{w_i} \cdot \vec n)))^e[/cht]
в таком виде формула называется "modified phong". обратите внимание, что в уравнении рендеринга она вся умножается на [cht]n \cdot l[/cht], то есть примерно так:
[cht]L(\vec w_i) = \int BRDF(\vec{w_i}, \vec{w_o}, \vec{n})(\vec n \cdot \vec{w_o})L(\vec {w_o}) \vec{d\omega_o}[/cht]

У меня возникло несколько сомнений по поводу этого подхода и для проверки я написал предельно влобный path tracer, который единственное, что умеет — это просто выпускать по монте-карло лучи, даже без importance sampling'а. И мои опасения толь оправдались. Вот так выглядит написанная выше модель "modified phong", считающаяся классическим способом нормализации фонга:
Изображение
В этом тесте у шара справа [cht]k_d = 0, \ k_s = 1,\ e = 50[/cht]
нетрудно заметить, что при устремлении парамера экспоненты к бесконечности, энергия-то ни черта на практике не сохраняется. следует ожидать, что при большой экспоненте должна быть поверхность примерно зеркальная, то есть весь свет должен уходить только в одно направление, но получается так, что из-за множителя [cht](n, l)[/cht] отражается только часть, помноженная на [cht](\vec n \cdot \vec{w_o})[/cht], в итоге немного предсказуемо при скользящих углах обзора у объектов получаются тёмные области на границах.

Существует также другая модель, называющаяся просто моделью phong без приставки "modified". Эта модель считается ужасно отстойной, потому что, якобы, предложена фонгом ещё в каком-то там 1975 году и якобы работает плохо. Модель по сути заключается в том, что
[cht]BRDF(\vec{w_i}, \vec{w_o}, \vec{n})(\vec n \cdot \vec{w_o}) = k_d (\vec n \cdot \vec{w_o}) + k_s\frac{e+1}{2\pi} (\vec{w_o} \cdot (\vec{w_i} - 2\vec n (\vec{w_i} \cdot \vec n)))^e[/cht], то есть множитель [cht](\vec n \cdot \vec{w_o})[/cht] отсутствует при specular члене. Выглядит эта модель так:
Изображение
То есть для теста с зеркалом она выглядит правильно(тёмная каёмочка — модель даёт ошибку из-за конечной величины экспоненты, не обращайте внимания). Однако, если положить, что параметр экспоненты очень мал, то следует ожидать, что specular компонента перейдёт примерно в diffuse, при этом, у второй модели (та, что phong без modified), освещённость переходит в константу, что неверно, зато у первой(modified phong) — всё нормально, переходит в диффуз.

Кто виноват? Что делать?

Правка: 16 мая 2018 15:34

Great V.Постоялецwww16 мая 201815:17#1
Suslik
> в итоге немного предсказуемо при скользящих углах обзора у объектов получаются
> тёмные области на границах
А оно вот оно оказывается че! Все таки это лыжи не едут!
А я уже думал что это у меня руки из жепы растут.
}:+()___ [Smile]Постоялецwww16 мая 201816:35#2
Suslik
> нетрудно заметить, что при устремлении парамера экспоненты к бесконечности, энергия-то ни черта на практике не сохраняется.
Собственно, как я понимаю, именно поэтому фонг обычно и не используется.

> Кто виноват? Что делать?
Попробуй Blinn вместо фонга.

FROLПостоялецwww17 мая 20180:13#3
>> Кто виноват?
>> что в уравнении рендеринга она вся умножается на [cht]n \cdot l[/cht], то есть примерно так:

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

То есть он вообще не относится к типу BRDF - будь то фонг или блинн, они все будут давать это потемнение на краях.

>> Все таки это лыжи не едут!
У меня тоже не едут, да.

>>Что делать?
Это конечно куда более интересный вопрос.
Обычно пишут что-то про невычислимый предел и зеркальное отражение заменяют на специальный случай, где этого косинуса нету.
В SmallVCM например прямо внутри BRDF делят на косинус, чтобы потом в интеграле он сократился.

Более трудный вопрос -- что делать в переходном случае, вроде как и не зеркало но и не шероховатый фонг.
Я сигмоиду засунул, нормально работает.

 
static inline float sigmoid(float x)
{
  return 1.0f / (1.0f + exp(-1.0f*x));
}

static inline float sigmoidShifted(float x)
{
  return sigmoid(20.0f*(x - 0.5f));
}

// this is needed for high gloss value when it is near mirrors
//

static inline float PreDivCosThetaFixMult(const float gloss, const float cosThetaOut) // gloss is in interval [0, 1]
{
  const float t       = sigmoidShifted(2.0f*gloss);
  return 1.0f + t*(1.0f / fmax(cosThetaOut, 1e-5f) - 1.0f);
}

....
  const float cosLerp = PreDivCosThetaFixMult(gloss, cosThetaOut);
  a_out->color        = color*((cosPower + 2.0f) * INV_TWOPI * pow(cosAlpha, cosPower))*cosLerp;

Правка: 17 мая 2018 0:14

SuslikМодераторwww17 мая 20183:24#4
FROL
> На сколько я знаю, косяк в том что это уравнение (основное уравнение
> рендеринга) не работает для предельного случая -- идеального зеркала, где
> получается неопределённость типа ноль на ноль.
должна получиться дельта-функция в пределе. проблема вылазит именно из-за косинуса и я не верю, что уравнение рендеринга не в состоянии описать зеркальную поверхность.

> То есть он вообще не относится к типу BRDF - будь то фонг или блинн, они все будут давать это потемнение на краях.
нет, не верю.

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

FROL
> Обычно пишут что-то про невычислимый предел и зеркальное отражение заменяют на
> специальный случай, где этого косинуса нету.
предельный случай — это [cht]\lim_{e \rightarrow +\infty}e\cos^e(\alpha)[/cht]. а косинус [cht](\vec n \cdot \vec l)[/cht] от предела вообще не зависит, он там всегда. то есть в моём представлении проблема не в вычислимости предела, а в том, что уравнение в пределе сходится к какой-то ерунде.

Правка: 17 мая 2018 4:00

DelfigamerПостоялецwww17 мая 20186:44#5
FROL
> > > о делать?
> Это конечно куда более интересный вопрос.
Мне кажется, самое естественное решение - переносить коэффициенты из усиления в распределение, по которому выбирается направление луча.
А так, в каком-то смысле, - это проблема дискретизации, когда разрешения (количество сэмплов на переотражение) не хватает, чтобы поймать мелкую деталь в функции (узкий пик в BRDF).
SuslikМодераторwww17 мая 20188:39#6
и снова приз отправляется }:+()___ [Smile]'у за
> Попробуй Blinn вместо фонга.
как выяснилось, в том же Substance Painter'е/Marmoset'е используется именно он. я думал, что это волшебство с half-vector'ом используется, чтобы упростить расчёт, а оказалось, что он точнее результат даёт именно для случая низкого значения экспоненты.
FROLПостоялецwww17 мая 201811:48#7
Suslik
> я не верю, что уравнение рендеринга не в состоянии описать зеркальную
> поверхность.

Возможно я неточно выразился (я сказал "не работает").
Описать оно как раз в состоянии, но описать и посчитать -- это разные вещи.

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

>> то есть в моём представлении проблема не в вычислимости предела, а в том, что уравнение в пределе сходится к какой-то ерунде.
Звучит как будто бы это одно и то же ...

FROLПостоялецwww17 мая 201812:01#8
Suslik
> должна получиться дельта-функция в пределе.

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

SuslikМодераторwww17 мая 201812:20#9
FROL
> Звучит как будто бы это одно и то же ...
разница очень большая. если бы проблема была в вычислимости интеграла, это бы обозначало, что его трудно посчитать. но посчитать-то его нетрудно — достаточно устремить экспоненту к бесконечности. хоть его и нельзя посчитать точно, но можно посчитать с любой необходимой точностью. отстой в том, что посчитанный результат сходится не к тому, что хочется, а к затемнённой по краям ерунде, с какой точностью ты его ни считай.

например, из-за этого даже при большом, но конечном значении экспоненты вроде e = 100, результат вполне можно посчитать и он выглядит неправильно(см изображение в #0), то есть проблема совсем не в пределе.

Правка: 17 мая 2018 12:21

/ Форум / Программирование игр / Физика

2001—2018 © GameDev.ru — Разработка игр