W trakcie tych zajęć zajmiemy się implementacją modelu oświetlenia Phonga. Na poprzednich zajęciach zbudowaliśmy układ słoneczny. Wykorzystamy go w trakcie tych zajęć. Jeżeli go zrobiłeś, to przekopiuj do ex_5_1.hpp
kod z poprzednich zajęć. W przeciwnym wypadku wykorzystaj ten, który jest zaimplementowany w ex_5_1.hpp
. W zadaniu będzie nam potrzebny statek latający przed kamerą, jak nie ma tego w Twojej scenie to skopiuj to z ex_5_1.hpp
.
Oblicz we fragment shaderze oświetlenie obiektu przy użyciu modelu Phonga dla odbicia rozproszonego (*diffuse)
1. Przekaż źródło światła. Na razie przyjmiemy kierunkowy model oświetlenia, dlatego źródło będzie opisane jako wektor kierunkowy:
Prześlij do shadera fragmentów zmienna typu uniform vec3
(nazwij ją np. lightDir
), w której będzie się znajdować wektor kierunkowy.
Należy to zrobić podobnie do tego, jak przesyłany jest kolor.
Jako kierunek światła wybierz dowolny wektor jednostkowy. (Możesz wziąć dowolny niezerowy wektor następnie go znormalizować).
Dodatkowo prześlij drugą zmienną uniform vec3 lightColor
, w której umieścimy kolor światła. Prześlij tam wartości ze zmiennej glm::vec3 lightColor
.
prześlij normalne z shadera fragmentów do shadera wierzchołków
znormalizuj wektor normalny przed użyciem go w obliczeniach (uśrednianie wektorów normalnych wierzchołków może spowodować, że przestaje one być jednostkowe).
Natężenie to iloczyn skalarny wektora normalnego powierzchni i odwrotnego wektora kierunku padania światła. Skorzystaj z funkcji dot
.
Natężenie nie może być ujemne. Przytnij natężenie do zera przy użyciu: x = max(x, 0.0)
lightColor
.Dlaczego oświetlenie statku nie zmienia się podczas jego obracania?
(Wektory normalne są w układzie lokalnym modelu, a wektor padania światła w układzie świata)
Należy wykonać transformacje wektorów normalnych do przestrzeni świata: - Prześlij macierz modelu rysowanego obiektu (model Matrix) jako osobna zmienna do vertex shadera (uniform mat4
).
- Przemnóż przez te macierz wektor normalny wierzchołka przed przesłaniem go do shadera fragmentów. - Współrzędna w dopisana do wektora przed mnożeniem przez macierz powinna być ustawiona na 0. Wynika to z tego, że w przypadku transformacji wektorów reprezentujących kierunki w przestrzeni, nie chcemy dokonywać translacji — np. wektor normalny do powierzchni zależy od orientacji obiektu, ale nie od jego pozycji (przesunięcia) w przestrzeni świata.
Uzupełnił model o czynnik odbicia zwierciadlanego (specular). W tym celu:
cameraPos
) jako kolejna zmienna do fragment shadera.vertexPosition
) w przestrzeni świata (czyli pomnożone przez macierz modelMatrix). Pamiętaj, że tym razem wektory reprezentują punkty, a nie kierunki - współrzędna w przed mnożeniem musi być ustawiona na 1. W wyniku rasteryzacji otrzymamy w shaderze fragmentu jego pozycję (nazywaną pozycją fragmentu)reflect
. Pamiętaj, żeby przesłać do funkcji odwrócony wektor kierunku światła.max(...,0.0)
), a następnie podniesiony do wysokiej potęgi (np. 8, 50, 1000), która jest miara połyskliwości powierzchni.objectColor * diffuse + lightColor * specular
. Oznacza to najprostszy przypadek, gdy kolor światła odbitego jest biały.W układzie planetarnym obiektem oświetlającym powinno być słońce, dlatego zamień oświetlenie kierunkowe na punktowe:
lightDir
) kierunek światła, prześlij pozycję słońca do fragment shadera (taką jak ustawiłeś w punkcie powyżej) jako uniform vec3 (nazwij go lightPos
).lightDir
.Źródło światła znajduje się wewnątrz kuli, która reprezentuje słońce, dlatego jest czarna. By to naprawić, utwórz osobny shader, który będzie odpowiadać za renderowanie słońca.
Celem tego zadania jest stworzenie shadera (shader_5_sun.vert_ i shader_5_sun.frag), który będzie odpowiadał wyłącznie za rysowanie słońca. Poprzednie shadery (shader_4_1.vert_ i shader_5_1.frag) nadal mają rysować pozostałe obiekty. a) zainicjalizuj program (shadery): - Pliki shader_5_sun.vert i shader_5_sun.frag są identyczne z 5_1** przed zmianami, będą punktem wyjścia dla shadera słońca.
Utwórz zmienną globalną GLuint programSun
na adres shadera słońca. Stwórz program za pomocą shaderLoader.CreateProgram
analogicznie jak tworzy się program
z shader_5_1.vert i shader_5_1.frag (parametry wejściowe to ścieżka do shadera wierzchołków i fragmentów shader_5_sun.vert i shader_5_sun.frag).
W skomplikowanym projekcie różne typy obiektów rysuje się przy użyciu różnych shaderów, zate potrzebna jest w programie architektura, która na to pozwala. Ustaw odpowiedni program (shadery) do rysowania słońca:
drawObject
korzysta z globalnej zmiennej program
, żeby wskazywać shadery do rysowania. Dodaj argument do funkcji, który będziesz przekazywać adres programu, który ma być wykorzystany do rysowania.drawObject
.Na poniższym obrazku jest zdjęcie słońca. Jest ono ciemniejsze na brzegach spróbuj uzyskać podobny efekt. Przydadzą się wektory normalnych i wektor V jak i funkcja mix.
Światło pochodzące z punktowego źródła traci na sile wraz z dystansem. Wynika to z tego, że rozprasza się na większą powierzchnię. Dodaj ten efekt do shadera. Zamiast brać kolor światła bezpośrednio, podziel go przez kwadrat dystansu od źródła świata. Przed kwadratowaniem przemnoz diystans przez 10.0.
Przez obecną zmianę scena stała się ciemna. Wymaga to od nas zmiany ‘koloru’ światła na wartości dużo większe niż do tej pory. Jeśli teraz to zrobimy i przesadzimy w drugą stronę, otrzymamy efekt prześwietlenia. Wynika to z ograniczenia zakresu kolorów do \([0,1]\) (obsługą wyższych wartości nazywamy HDR). Rozwiązaniem jest pracowanie na wartościach powyżej 1 wykorzystanie tone mappingu do przeniesienia ich w zakres \([0,1]\). Istnieje wiele wzorów, które są wykorzystywane do tego, jeden z nich to:
\[C_{mapped} = 1-e^{-C * E},\] gdzie C to kolor sceny a E to parametr ekspozycji (z zakresu \((0,\infty)\), który może być dostosowany w zależności od jasności.
Dodaj drugie źródło oświetlenia w postaci reflektora statku. Reflektor świeci tylko w określonym stożku,dlatego oprócz pozycji spotPos
posiada również kierunek spotDir
i kąt świecenia \(\phi\). Po obliczeniu dla niego lightDir
należy sprawdzić, czy iloczyn skalarny pomiędzy lightDir
a spodDir
jest większy niż \(\cos\phi\) . Jeżeli nie jest, to stożek nie świeci w tym miejscu. Można ułatwić sobie implementację wielu źródeł światła poprzez przeniesienie obliczeń oświetlenia do funkcji, która przyjmuje kierunek światła i siłę naświetlenie.