ДР/2011/Л6/Зад. 2

В коментарите след лекция 6 бях писал за новия код в Reflection шейдъра:

	int actual_samples = ray.depth == 0 ? this->samples : 5;

Причината за него е следната.
Нека имаме сегашната примерна сцена. На моята машина, тя се ренди за 20 секунди. Ако върнем кода така, както беше на лекцията:

	int actual_samples = this->samples;

времето скача на 62 секунди. Същевременно се вижда, че основната част от времето се губи в регионите точно под сферата и в непосредствена близост до нея.

Какво, всъщност, се случва там?
Идващите от камера лъчи попадат първо в пода, и шейдърът пуска множество лъчи, за да оцени грапавото отражение:

Голяма част от тях стигат до сферата. Там, стъкления шейдър разделя лъчите на две. Пречупените не са проблем, но отразените отиват обратно до пода и там отново става мощно семплиране с много лъчи:

Така, само един лъч от камерата предизвиква лавина от хиляди лъчи. Същевременно е ясно, че не необходимо смятането на толкова много лъчи (един Color едва ли би могъл да обхване толкова информация), съществено е да "смятаме с пълна точност" само първото грапаво отражение, а следващите можем да минем и с по-малко лъчи.
Точно това прави и поправката горе.

Само че, има проблем.
Лъчите, които се пречупват право през топчето и стигат до пода, също се подлагат на "маловажна третировка", при това незаслужено - техният резултат се вижда добре във финалния кадър. В резултат, областта от пода, пречупена през топчето, е видимо шумна:

Още по-ярък пример може да си създадем, ако премахнем грапавостта на пода (0.97 -> 1.0), а на самото топче сложим само Reflection шейдър с glossiness 0.90:


Ясно се вижда "разликата в качеството" на самото топче и на отражението му!

Задачата е да измислите и реализирате някакъв подход, с който да може да определите дали лъчът си струва да се семплира качествено, или не, и който подход да решава всички показани случаи: да не се отказваме съвсем от оптимизацията (и да предотвратяваме комбинаторната експлозия в първия пример), но да си възвърнем качеството в примери №2 и №3.

Очаква се да получите нещо такова като резултат: пример 2, пример 3.

Времето за рендериране не трябва да се увеличава значително - не повече от 30% спрямо оригинала.

Подобно на задачка №0, тук има всевъзможни начини да постигнете исканото в условието. Няма да ви развалям удоволствието да си ги проучите сами; но, отново, ако стигнете до задънена улица или имате проблем, ще помагам(е), а подсказка мога да добавя след няколко дни, ако изявите желание.

Comments

Подсказка

Може ли подсказката :)

flags

Добавете възможност да "маркирате" лъчите (още едно поле в класа Ray). Стартиращите от камерата лъчи са немаркирани. При достигане на шейдър с грапаво отражение, маркирайте резултатните лъчи, които ще пускате рекурсивно надолу. Така, ако тези лъчи стигнат обратно до glossy повърхността, ще видите, че са маркирани, и семплирате с ниско качество; немаркираните третирате с пълно качество.
Трябва да проверите дали навсякъде из raytracer-а, "новоизлюпващите се" лъчи се създават правилно, т.е. въпросните флагове се предават при рекурсивните извиквания на raytrace().

П.П.: решението ви може да е по-генерално: добавеното поле може да държи повече от един флаг (реализирайте отделните флагове като отделни битове от един int, например). Така ще може да се маркират лъчите, които изстрелваме за проверка на засенчване (shadow rays - например с флаг RT_SHADOW). По-нататък, intersect() методите на геометриите могат да проверяват за RT_SHADOW и не генерират UV координати, нормали и пр. за така маркирани лъчи.