# Shadow mapping Cienie są ważnym elementem oświetlenie. Dodają realizmu do sceny i dzięki nim łatwiej jest graczowi zorientować się w przestrzennym rozłożeniu obiektów. ![](./img/shadows1.bmp) Na powyższym obrazku możesz zobaczyć, że dużo łatwiej jest okreśłić położenie kostek, gdy rzucają one cienie. Podstawową techniką generowania cieni jest **shadow mapping** i wiele bardziej zaawansowanych technik na niej bazuje. Składa się on z dwóch kroków. W pierwszym obliczamy mapy głębokości z perspektywy źródła światła i zapisaniu do tekstury (tą teksturę nazywamy *shadowmap*), w drugim przy rysowaniu fragmentu porównujemy jego odległość do źródła światła z odległością zapisaną w teksturze. Celem tych zajęć będzie dodanie cieni do początkowej sceny. W obecnym projekcie są 3 źródła światła: światło słoneczne, reflektor samolociku i lampa planetarna. W trakcie zajęć skupimy się na świetle słonecznym. ## Mapa głębokości - Framebuffers Framebuffer to obiekt, do którego rednerowana jest scena w postaci tekstury. Do tej pory korzystaliśmy z domyślnego Famebuffora, który był wyświetlany na ekranie. Teraz potrzebujemy dodatkowy, który będzie przechwytywał mapę głębokości. Tworzymy go w następujący sposób ```C++ glGenFramebuffers(1, &depthMapFBO); ``` Zmienna `depthMapFBO` jest jak to typu `unsignet int` i powinna być dostępna globalnie. Kolejnym krokiem jest stworzenie tekstury głębokości ```C++ glGenTextures(1, &depthMap); glBindTexture(GL_TEXTURE_2D, depthMap); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); ``` Również zmienna `depthMap` jest jak to typu `unsignet int` i powinna być dostępna globalnie. Tworzymy teksturę, zaznaczamy, że jest to tekstura głębokości nadając jej format `GL_DEPTH_COMPONENT`. Parametry `SHADOW_WIDTH`, `SHADOW_HEIGHT` są ustalone globalnie i oba wynoszą 1024. I w końcu podpinamy teksturę pod FBO. ```C++ glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, 0); ``` ### Zadanie Utwórz funkcję `initDepthMap`, w której zainicjalizujesz mapę głębokości. Wywołaj ją w funkcji `init`. ### Renderowanie mapy głębokości W tej części będziemy uzupełniać funkcję `renderShadowapSun`. Funkcja ma za zadanie zapisać w FBO mapę głębokości z perspektywy słońca. Pierwsze co musimy mieć, to parę shaderów, która będzie renderować mapę. Ponieważ jedyne co potrzebujemy tylko rozmieścić obiekty w odpowiednich miejscach. Shader wierzchołków ustawia tylko pozycję w oparciu o przesyłanie macierze. ```C++ #version 430 core layout(location = 0) in vec3 vertexPosition; layout(location = 1) in vec3 vertexNormal; layout(location = 2) in vec2 vertexTexCoord; uniform mat4 viewProjectionMatrix; uniform mat4 modelMatrix; void main() { gl_Position = viewProjectionMatrix * modelMatrix * vec4(vertexPosition, 1.0); } ``` Natomiast shader fragmentów jest pusty, ponieważ nic nie wysyłamy a głębokość zapisywana jest automatycznie. ```C++ #version 430 core void main() { } ``` ### Zadanie Dodaj utwórz parę shaderów jak powyżej, załaduj je do zmiennej globalnej o nazwie `programDepth` i aktywuj go w funkcji `renderShadowapSun`. Utwórz funkcję `drawObjectDepth`. która będzie przyjmować referencję do `RenderContext`, macierz `viewProjection` i macierz modelu oraz przesyłać macierze do GPU i rysować `RenderContext` Musimy zdefiniować macierz widoku i rzutowania, które mamy przesłać. Implementujemy cienie dla oświetlenia kierunkowego, gdzie dla każdego punktu kierunek światła jest taki sam. W takim wypadku skorzystamy z rzutowania prostopadłego. ![](./img/shadow_mapping_projection.png) Do stworzenia macierzy rzutowania perspektywicznego wykorzystamy funkcję: ```C++ glm::mat4 lightProjection = glm::ortho( float left, float right, float bottom, float top, float zNear, float zFar ) ``` Tworzy ona macierz rzutowania prostopadłego dla zadanych wymiarów. Musimy tak je dobrać, żeby rzutowanie zawierało całą interesującą scenę. Za małe wartości spowodują artefakty a za duże pogorszą jakość. Przykładowo możesz wziąć `glm::ortho(-10.f, 10.f, -10.f, 10.f, 1.0f, 30.0f)`. Do stworzenia macierzy kamery wykorzystamy funkcję `glm::lookAt` z poniższymi argumentami argumentami ```C++ glm::lookAt(sunPos, sunPos - sunDir, glm::vec3(0, 1, 0)) ``` ### Zadanie Uzupełnij funkcję `renderShadowapSun`. Wywołaj w niej instrukcje ```C++ //ustawianie przestrzeni rysowania glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); //bindowanie FBO glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); //czyszczenie mapy głębokości glClear(GL_DEPTH_BUFFER_BIT); //ustawianie programu glUseProgram(programDepth); ``` Stwórz macierz `viewProjection` ```C++ glm::mat4 lightVP = glm::ortho(-10.f, 10.f, -10.f, 10.f, 1.0f, 30.0f) * glm::lookAt(sunPos, sunPos - sunDir, glm::vec3(0, 1, 0)); ``` następnie wywołaj `drawObjectDepth` dla każdego obiektu, który rysujemy w naszej scenie. wykorzystaj macierz `viewProjection` zdefiniowaną wyżej, użyj tej samej macierzy modelu co przy właściwym rysowaniu. Zakończ funkcję linią `glBindFramebuffer(GL_FRAMEBUFFER, 0);`, która przywraca domyślny FBO. ### Wizualizacja mapy głębokości odkomentuj linie: ```C++ //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //glUseProgram(programTest); //glActiveTexture(GL_TEXTURE0); //glBindTexture(GL_TEXTURE_2D, depthMap); //Core::DrawContext(models::testContext); ``` znajdujące się w `renderScene`. Rysują one prostokąt z mapą głębokości jako teksturą. Jeżeli wszystko zostało wykonane poprawnie, to powinien on zawierać rzutowanie naszego pokoju. ### Zadanie W tej chwili rzutowanie jest nieoptymalne. popraw je na lepsze. Zmodyfikuj wartości w `glm::ortho(-10.f, 10.f, -10.f, 10.f, 1.0f, 30.0f)` do takich, żeby pokój wypełniał jak największą część tekstury. ### Rysowanie cieni Na tym etapie powinniśmy mieć poprawnie stworzoną mapę głębokości. Pozostaje wykorzystać ją w oświetleniu. ### Zadanie #### Przesłanie danych W funkcji `drawObjectPBR` prześlij teksturę za pomocą instrukcji: ```C++ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, depthMap); ``` oraz macierz `LightVP`, która musi być taka sama jak w `drawObjectDepth`. Następnie w shaderze wierzchołków odbierz `LightVP`. #### Shader wierzchołków Oblicz pozycję wierzchołka z perspektywy słońca `sunSpacePos=LightVP*modelMatrix*vec4(vertexPosition,1)`, wynik prześlij do shadera fragmentów #### Shader fragmentów odbierz `sunSpacePos` i napisz funkcję `calculateShadow`, która sprawdza czy obiekt jest zacieniony. Aby to zrobić kolejno w funkcji: * ujednorodnij zmienną `lightSpacePos` dzieląc ją przez współrzędną **w**, * przeskaluj ją, ma wartości od -1 do 1 a potrzebujemy wartości od 0 do 1 (pomnóż przez 0.5 i dodaj 0.5) wynik zapisz do zmiennej `lightSpacePosNormalized`, * pobierz głębokość z `depthMap` próbkuj za pomocą współrzędnych **x** i **y**. Pobierz tylko kanał `r`, zapisz go do zmiennej `closestDepth`, * porównaj `closestDepth` ze współrzędną **z** `lightSpacePosNormalized`. jeżeli `closestDepth` jest większa zwróć 1.0, w przeciwnym wypadku zwróć 0.0. * wynik funkcji przemnóż z `sunColor` w trakcie oblicznia. ``` ilumination=ilumination+PBRLight(sunDir,sunColor,normal,viewDir); ``` #### shadow acne Powinniśmy dostać cienie , jednak w niezacienionych strefach pojawiły się paski, które znane są jako *shadow acne* wynikają one z błędu przybliżenia liczb zmiennoprzecinkowych. Można się go pozbyć na dwa sposoby 1. dodać bias. zamiast sprawdzać `closestDepthlightSpacePosNormalized`, gdzie `bias` to mała wartość (np 0.01). 2. innym rozwiązaniem jest, żeby przy renderowaniu cieni włączyć front face culling. dzięki temu rysowane będą część modelu, które są dalej niż te, które odpytujemy. ### Zadanie* Dodaj rysowane cieni również dla latarki doczepionej do statku. pamiętaj, że musisz wykorzystać tutaj macierz rzutowania perspektywicznego