Вълни по водна повърхност

Създайте специален шейдър, който да имитира водна повърхност (приема се, че ще се закачи на плоска геометрия, най-обикновен Plane, който няма да се завърта по никакъв начин, трансформацията му ще е идентитет).

В най-простия вариант на задачата, шейдърът трябва да се държи като обикновен Reflection шейдър, който, преди да пресметне отразения лъч, да променя леко нормалата (така, че тя да варира леко около (0, 1, 0)), с цел да постигне вълнообразните ефекти. Трябва да се симулират следните два ефекта:

1. Слаби, "случайни" вълнички, подобни на вълнението на море. Трябва ви съвсем леко отколонение на вертикалната нормала, като самото отклонение трябва да генерирате на базата на позицията на точката, която шейдвате (info.ip). За генериране на тези случайни отклонения може да ползвате подход, подобен на този в WaterBumps текстурата (с наслагване на няколко синусоиди с дисонантни честоти).
2. При всички случаи, важно е "вълните" да са анимирани, т.е. в интерактивен режим да изглежда, сякаш водата се "движи" и "има вълнение". За да симулирате това, в renderFrame добавете код, който слага текущото време в някаква глобална променлива (например, добавете един double time към класа Scene, така ще я имате навсякъде - scene.time). Текущото време се изчислява на базата на SDL_GetTicks(), като в началото на програмата то е 0, след 1 секунда е 1 и т.н. За да анимирате вълните, после в самия шейдър е необходимо, при генерирането на отклоненията, в аргументите на sin() да участва и времето, при това умножено по различна честота за всеки екземпляр от синусите (тези честоти също може да си ги изберете случайно, може даже да са отрицателни - важното е да са дисонантни, или поне да не са еднакви всичките).

За да си тествате реализацията, модифицирайте сцената simple.hexray (или си направете друга за целта). Ползвайте вашия шейдър за равнината отдолу (припомням, че вашият шейдър трябва да е клас, наследяващ Shader, и освен това не забравяйте да го регистрирате в DefaultSceneParser::newSceneElement). Ако си пуснете сцената в интерактивен режим (interactive 1 в GlobalSettings), трябва да видите анимираните водни вълнички (виж клипчето долу за пример, първите 5 секунди).

Тази базова задача носи 10т.

Бонус 1 (5т)
Създайте възможност да се симулират вълничките от паднали водни капки (пример). Водните капки генерират концентрични кръгове, които се раздалечават и затихват с времето. За целта, във вашия шейдър вкарайте и малко данни за тези капки (позиция на падането на всяка капка, и времето в което тя ще падне, спрямо scene.time). След като капката "падне" (dt > 0, където dt = scene.time - drop.time), то можете да пресметнете височината на генерираните вълнички по следния начин - ако dist е разстоянието между "епицентъра" и info.ip, то

x = dt * waterSpeed - dist (където waterSpeed определя скоростта на разпространение на вълничките)
След което, височината на вълната в точката, която шейдваме, е
waveFunction(x) / (1 + dt), където waveFunction е подходяща функция, която за x > 0 прилича на "вълна със затихване". Например, може да ползвате

waveFunction(x) := 3 * sin(x) / (2 + 3*x)

Чрез waveFunction можете да пресметнете производната в точката x, а чрез тази производна можете да сметнете отклонението от вертикалната нормала, което waveFunction предизвиква (отклонението е в посока от info.ip към епицентъра).

Как точно ще определите къде и кога да "падат капките" е по ваше усмотрение. Може и да са хардкоднати, а може и да се инициират от потребителя по подходящ начин. Вижте долу за клипче, което показва 3 капки (в случая са хардкоднати)

Бонус 2 (5т)
Освен да отразява, водата може и да пречупва светлината. Този бонус изисква от вас да симулирате напълно водата, като пускате два лъча (пречупен и отразен) - като в какво отношение да смесвате резултатите от raytrace() за двата лъча пресмятате чрез fresnel() фунцията (разгледайте кодовете на Fresnel текстурата и на Refraction шейдъра и не би трябвало да имате проблеми да ги адаптирате за вашия случай).

Можете да реализирате и двата бонуса (така може да получите сумарно 20т от задачата).

Пример:
Ето video, в което са реализирани и двата бонуса.

Comments

Сцена за проекта

Не мога да намеря сцената heightfield.qdmg (или heightfield.fray). Надявам се няма проблем да започна да го пиша, използвайки някоя друга.

Няма проблем

Да, няма проблем да се направи друга сцена.

Проблем с базовия шейдър близо до хоризонта

Реализирам базовата задача (тази, в която Water shader-ът се държи като Reflection). Проблемът, с който се сблъсквам е, че в далечината на сцента, т.е. там където лъчите са почти хоризонтални, наклонената нормала може да причини отражение, което сочи надолу. Илюстрация - http://imageshack.us/photo/my-images/196/normalsproblem.jpg/ Резултатът от това са черни петна. Искам да попитам дали това е нормалното и очаквано поведение на шейдъра, или нещо пропускам.
Благодаря.

Да, това е очаквано да се

Да, това е очаквано да се получи като проблем.
Иначе можеш да го решиш просто, като ако засечеш този случай, да пресметнеш наново отражението, като вече приемеш, че нормала е неотместен, т.е. (0, 1, 0). Така отраженията ще са си "нагоре" винаги.