Лекция 12::Задача 2

Отражателен BRDF

Реализирайте BRDFMirror, BRDF-a съответстващ на Reflection shader с glossiness = 1 (перфектно огледало).

За целта, в сцената gi_test.fmiray добавете Reflection шейдър и го закачете към една от топките. При изключено GI, резултатът трябва да е очаквания при огледална сфера. При включено GI, обаче, огледалната топка ще е черна, тъй като нямаме написан огледален BRDF. За да го реализирате, добавете метода getBRDF() към класа Reflection (по подобие на Flat) и в него връщайте инстанция на BRDFMirror (игнорирайте glossiness параметъра - приемете, че винаги е 1). Методите eval() и spawnRay() на BRDFMirror реализирайте по следния начин:

eval() - тъй като, както споменахме, brdf-а на чистото огледало е ненулев само в една единствена ситуация - когато w_out е точно отражение на w_in спрямо нормалата на повърхността, то в eval принципно трябва да взимате отражението на w_in, и ако то съвпадa с w_out, да връщате безкрайност (защото там плътността е безкрайна; все едно да имате нормално разпределение със сигма = 0, и да го измерите в средата му). Във всички други случаи връщате Color(0, 0, 0).
Обаче - заради сравнението на floating point числа (две FP числа са равни само ако съвпадат дословно), шансовете да се падне такова съвпадение са практически 0. Даже и да пускате по милиони пътища през пиксел, шансовете да уцелите точно отражението са почти нулеви, а дори и да се случи, то ефектът ще е просто едно горещо-бяло пикселче, някъде по картинката.
Затова, в eval() може смело да връщате Color(0, 0, 0) във всички случаи. Съществената функция за този BRDF е spawnRay() - това е екстремен пример колко е важен importance sampling-а.

spawnRay() - тук трябва да върнете отразения лъч на w_in (вижте Reflection::computeColor). В параметрите color и pdf трябва да върнете, респективно, резултатите от eval() за върнатата посока, и нейната вероятностна плътност. Резултатът от eval() тук вече е безкрайност, умножена по "дифузния" цвят на материала (който е Color(1, 1, 1) за перфектно огледало, а може и Color(0.97, 0.97, 0.97) ако искате да вкарате малко повече реализъм и да допуснете 3% загуба при отражението). pdf-а също е безкрайност, тъй като вероятността да изберем точно тази посока е безкрайна (тя е единствена) (пак примера с нормално разпределение при сигма = 0).

Тук имаме малък проблем - връщаме две безкрайности, които по-нататък в сметките скрито се делят една друга, но при floating point числата не може да делим безкрайност на безкрайност. За това може да ги "съкратим" предварително, при което за color връщаме отражателността на материала (както споменахме горе), а за pdf връщаме 1. Ако сте пуристи, може просто да ги върнете двете, умножено по много голямо (но крайно) число.

Ако не променяте другите насторйки на gi_test сцената, ще получите нещо такова: primer

Comments

fixed

/* Горе-долу колко време би трябвало да отнеме за да се рендне gi_test от ревизя 292? Защото след като ъпдейтнах и компилирах сцената за 15 минутки ми беше изрендило само 10% от цялата сцена, а дори не беше стигнало до топките, където очаквам да се бави повече...
*/

В release rend time спадна на 5 мин 20 сек

Ние нали си имаме такива

Ние нали си имаме такива функций вече направени на тяхно място ли да направим новите..? И какво е Gl това glossines параметъра ли е ?
"В параметрите color и pdf трябва да върнете, респективно, резултатите от eval() за върнатата посока, и нейната вероятностна плътност" хм нещо не разбирам нали eval() връщаше само Color(0,0,0) от къде дойдоха тази посока и плътност?

Също така пише че трябва да върнете отразения лъч на w_in в тази функция eval() която си е зададена си подаваме параметър w_in но тази функция която се иска да я реализираме няма параметри от къде да го вземем този w_in лъч.. просто да го зададем ли?

Подред на номерата: 1) Имаме

Подред на номерата:

1) Имаме такива функции (шейдърите), но те са само за директно осветление, а за алгоритъма path tracing ни трябват BRDF-и, които все още нямаме написани за всички материали. Т.е., за всеки материал, трябват ни два класа - един шейдър (който се ползва от raytrace(), за директно осветление), и един BRDF, който се ползва от pathtrace(), за глобалното осветление. Т.е., в задачата се иска да реализирате още едно класче, наследник на BRDF, нищо друго.
2) GI = Global Illumination (Глобално осветление).
3) Значи, за искания отражателен BRDF, eval(info, w_in, w_out) трябва да връща Color(0, 0, 0), ако отразения лъч на w_in от повърхността на геометрията не съвпада с w_out. Практически за всички случайно взети w_in, w_out няма да имаме съвпадение, затова е и предложението ми да връщате Color(0, 0, 0) винаги.
Въпреки това, ако все пак w_out == reflected(w_in), то eval() трябва да върне следното нещо:

<отражателен цвят> * <вероятност w_in да се отрази по посока на w_out>

Както казахме, за отражателния цвят може да вземете Color(0.97, 0.97, 0.97) (огледалото губи 3% от светлината при отражение). А за вероятността, трябва да вземете безкрайност. Причината да вземете безкрайност идва от следния факт: при чисто огледало, идващата светлина от дадена посока се отразява само в една единствена друга посока, без да се разпръсква въобще. Т.е. вероятността даден входен лъч w_in да се отрази в някаква посока w_out е 0, освен ако w_out не съвпада с reflected(w_in). T.e. вероятността е ненулева само в "правилната" посока. От друга страна, интегралът от тези вероятности върху цялата полусфера трябва да се сумира до 1 (от фундаменталните изисквания за вероятност. Т.е. едно разпределение P(w_in, w_out), за да бъде правилна вероятност в нашия случай, трябва да изпълнява:

int
).

Така получената функция на разпределението на вероятността е специфична (нарича се Dirac delta function), и е нула навсякъде, с изключение на една точка, в която е безкраност.

4) Посоката и плътността идват от метода spawnRay() на BRDF-а, който трябва да реализирате. Виж коментарите за класа BRDF в началото на shading.h за да добиеш по-подробна представа какво се изисква от spawnRay().

5) Лъчът w_in си го подава pathtrace() алгоритъма. Няма нужда да го задавате вие. Иска се в spawnRay() да приемете входния лъч w_in и да генерирате отразения от повърхността лъч (който трябва да върнете от spawnRay метода).