ДР/2024/Л5/medium (5т)

Тук са описани средно-трудните домашни работи към лекция №5. Има 4 задачи (от които можете да решите само една - която ви се падне според факултетния номер). Задачите носят по 5 точки. Ползвайте таг homework5.


Група 0:

Реализирайте шейдъра Oren-Nayar. Подобно на Lambert, моделът има параметър за дифузен цвят, и още един параметър, sigma, който определя грапавината на обекта и варира от 0 до 1 (при 0 е идентичен с Lambert шейдъра). Обяснение на означенията в статията в Wiki:

  • Vi - e лъча от пресечната точка към лампата;
  • Vr - e лъча от пресечната точка към камерата;
  • Li - е яркостта и цвета на светлината, идващи в пресечната точка (и е както при Ламберт: lightColor * lightPower / distance²);
  • Lr - това е резултатът от шейдъра;
  • ρ/π - това са albedo и BRDF normalization - игнорирайте ги във вашата имплементация - приемете, че този член е 1;
  • θi - ъгъл между нормалата и Vi;
  • θr - ъгъл между нормалата и Vr;
  • ɸi и ɸr - азимутални ъгли на, респективно, Vi и Vr в равнината на повърхността на обекта. Представете си, че повърхнината на обекта е плоска (и, естествено, е перпендикулярна на нормалата). Проектирате векторите Vi и Vr в тази равнина - ще се получат два двумерни вектора, лежащи в нея. Търсеното cos(ɸi - ɸr) е всъщност косинуса от ъгъла между тези два двумерни вектора (като, когато този ъгъл е над 90° - цялата част от формулата с множителя B става 0).

С формулата от Wikipedia се смята подобрен вариант на функцията getLambertTerm. T.e. шейдърът ви ще се държи идентично като при Lambert, но вместо входящата светлина да умножавате по dot(lightDir, N), ще я умножавате по формулата в статията.

Ако ползвате следната сцена би трябвало да получите картинка като тази. Всички топчета са с цвят около (0.5, 0.5, 0.5), като розовите две са, респективно, с Lambert и Phong, а зеленикавите 5 са с Oren-Nayar и параметри sigma = 0.0, 0.2, 0.4, 0.6 и 0.8.


Група 1:

Да се добави възможност камерата да се позиционира чрез "lookAt" система.

Т.е. да се напише метод setPosition(), приемащ три параметъра:

- pos - позиция на камерата;
- lookAt - това е точка от сцената, която е "целта", към която гледа камерата. Т.е., лъча, изстрелян през средата на кадъра, трябва да отива в посока на точката lookAt;
- upDir - вектор, задаващ ориентацията "нагоре". Влияе, например, на roll ъгъла - досега в нашите сцени upDir беше винаги (0, 1, 0), но ако го зададем (0, -1, 0), би следвало кадъра да се обърне с главата надолу (но изборът не се изчерпва с тези двете, може да е произволен вектор, който не е успореден или срещуположен на (lookAt - pos).

Методът трябва да зададе стойности Camera::pos, както и на yaw, pitch и roll ъглите (може да приемете, че методът ще бъде извикан преди Camera::beginRender()).

За да тествате реализацията си, можете да пробвате да задавате за стойности на lookAt центровете на обектите в сцената - би следвало камерата да се обръща фронтално към тях.

Реализацията ви трябва да работи коректно и когато имаме gimbal lock - т.е., ако човек зададе на камерата да гледа право към пода, upDir трябва да работи, както би очаквал човек: очевидно upDir = (0, 1, 0) би било невалидно, но примерно между upDir = (1, 0, 0) и (0, 0, 1) подът би следвало да е завъртян различно.


Група 2:

Да се реализира възможността да имаме повече от една светлина в сцената (т.е. позициите, цветовете и интензитетите да се пазят в std::vector или масив, по подобие на nodes).
Аранжирайнте сцена с 2 или повече светлини - например, пробвайте да са с различни цветове и интензитети.


Група 3:

Да се добави опция дълбочината всеки пиксел (Z-value) да се визуализира на екрана, вместо цвета, върнат от raytrace() функцията.
За целта, модифицирайте raytrace() да връща и намерената минимална дълбочина по някакъв начин и записвайте тези стойности в масив, по подобие на vfb[][] масива.
В тестовата сцена в github ще има най-общо 3 групи пиксели: такива с безкрайна дълбочина (фона), такива с много голяма дълбочина - ≥10000 - близо до хоризонта, и такива с нормална.
Измислете подход, с който автоматично да намирате подходяща дълбочина maxDepth за следващата стъпка - например със статистически методи - 50ти или 75ти перцентил.
След което оцвете пикселите с цвят, като пикселите над maxDepth дълбочина оцветете в черно, а останалите - линейно от бяло (0 дълбочина) до черно (maxDepth).
Резултатът ще изглежда нещо подобно на това.