Oświetlenie Zaawansowane techniki oświetlenia, c.d. http://bazyluk.net/dydaktyka Gry komputerowe, Informatyka N1, III Rok
Image Based Lighting (IBL) Źródło obrazu: Panocapture.com, Automotive Pack 2
Image Based Lighting (IBL) Źródło obrazu: Panocapture.com, Automotive Pack 3
Image Based Lighting (IBL) Technika oświetlenia wykorzystująca mapę środowiska rzutowaną na powierzchnię oświetlanego obiektu Najczęściej jest to omnikierunkowe zdjęcie, zwykle HDR Pozyskanie mapy środowiska wymaga ultraszerokokątnego obiektywu lub specjalnego rodzaju kamery. Źródła obrazów: Panocapture.com, Automotive Pack Springer, Davide Scaramuzza. 4
Image Based Lighting (IBL) Technika oświetlenia wykorzystująca mapę środowiska rzutowaną na powierzchnię oświetlanego obiektu Najczęściej jest to omnikierunkowe zdjęcie, zwykle HDR Źródło obrazów: http://www.hdrlabs.com/sibl 5
Mapowanie środowiska Technika IBL wywodzi się z mapowania środowiska, które jest techniką rzutowania otoczenia obiektu pod postacią tekstury na jego powierzchnię Mapowanie sferyczne (ang. sphere mapping) Mapowanie sześcienne (ang. cube mapping) Źródło obrazów: Wikipedia http://escience.anu.edu.au/lecture/cg 6
Mapowanie środowiska Źródło obrazu: Gran Turismo Sony Computer Entertainment 7
Ambient Occlusion Dla każdego punktu obierana jest półsfera. W jej obrębie wysyłane są promienie. To, ile z nich i w jakiej odległości trafi w inne obiekty, sugeruje ile światła ze środowiska może dotrzeć do tego punktu. Kolejna, tym razem dokładniejsza niż phongowski komponent ambient, aproksymacja oświetlenia niebezpośredniego Estymacja tego, jak bardzo dane miejsce narażone jest na działanie oświetlenia pochodzącego ze środowiska (ang. ambient light) Rezultat może np. wpłynąć na wartość komponentu ambient w modelu Phonga Źródło obrazu: http://ucbugg.github.io/learn.ucbugg 8
Ambient Occlusion Technika ta nie bierze pod uwagę źródeł światła! Uwzględnia jedynie geometrię obiektów sceny i ich względne położenie oraz odległości Jest to technika nastawiona na rednerowanie offline Bazuje na koncepcji próbkowania / śledzenia promieni, jest więc bardzo kosztowna Im mniej próbek, tym większe zaszumienie Źródło obrazów: Nikki Ferchland, http://animationdynamics.com/ https://realtimeillumination.wordpress.com/ 9
Ambient Occlusion Źródło obrazów: Andrew Kramer 10
Ambient Occlusion Screen Space Ambient Occlusion (SSAO) Metody monte carlo pozwalają przyspieszyć działanie algorytmu. Operuje nie na geometrii, ale na zawartości bufora klatki dużo szybsze! Porównuje względne odległości blisko oddalonych od siebie pikseli korzysta z bufora Z Powoduje artefakty polegające na zmiennym oświetleniu obiektów zależnie od miejsca i kierunku, z którego je oglądamy Źródła obrazów: https://realtimeillumination.wordpress.com/ http://media.moddb.com/ 11
Screen-Space Ambient Occlusion (SSAO) Komponent Ambient w grze Starcraft II. Widoczne użycie SSAO. Zaszumienie obrazu wynika z zastosowanego rozkładu próbek. Rezultat jest później rozmywany z uwzględnieniem nieciągłości głębi. Źródła obrazu: Tatarchuk 08 Blizzard Entertainment 12
Deferred Shading Problem: W jaki sposób efektywnie obliczyć dynamiczne oświetlenie dla dużej liczby świateł i bardzo złożonej geometrii? 13
Forward Shading W klasycznym podejściu (ang. forward shading) oświetlenie jest obliczane dla każdego fragmentu powstałego przy renderowaniu kolejnych elementów sceny. W ten sposób dla każdego piksela bufora klatki wielokrotnie wykonuje się czasochłonne obliczenia zupełnie na darmo. Oczywiście stosowane są różne optymalizacje zmniejszające ten problem, jednak nie można go całkowicie wyeliminować. Geometria Shading Geometria Shading Geometria Shading Geometria Shading Geometria Shading Framebuffer Wydajność potoku jest więc ściśle zależna od złożoności geometrii, a także od liczby źródeł światła. 14
Deferred Shading Podejście z odroczonym cieniowaniem (ang. deferred shading) polega na przebudowie potoku tak, by właściwe cieniowanie odbywało się dopiero po wyrenderowaniu geometrii. Na pierwszym etapie obliczane są tylko podstawowe wartości, które posłużą później do właściwego obliczenia oświetlenia. Najbardziej kosztowne operacje związane z oświetleniem są zatem zawsze wykonywane tyle razy, ile mamy pikseli w buforze klatki (w uproszczeniu), a więc niezależnie od faktycznej złożoności geometrii. Geometria Geometria Geometria G-Buffer Shading Framebuffer Geometria Geometria 15
Deferred Shading Metoda odraczająca obliczenie oświetlenia do momentu, gdy będzie gotowa zawartość bufora klatki Dzieli proces renderowania klatki na dwa etapy: Obliczenie na podstawie geometrii sceny wartości, które będą niezbędne dla obliczenia oświetlenia Obliczenie właściwego oświetlenia z pomienięciem geometrii, już w przestrzeni ekranu Screen z UE3, prezentuje technikę Deferred Shading ze 123 dynamicznymi światłami (rok 2011) Źródła obrazów: Anton Lindeström Epic Games, UDK 16
Wynik obliczeń z pierwszego etapu przechowywany jest w tzw. G-Buffer. Zawartość G-Buffer: Deferred Shading Dopiero na podstawie jego zawartości obliczane jest faktyczne oświetlenie. Źródła obrazów: http://www.learnopengl.com 17
Deferred Shading Zalety Deferred Shading: Korzyści płynące z użycia podejścia deferred shading stają się znacznie większe, gdy rozważy się użycie techniki light culling. Light culling polega na renderowaniu wpływu źródeł światła tylko dla ograniczonego zasięgu. Np. renderując światło punktowe, przybliżamy jego kształt geometrią sfery. Złożoność obliczeń związanych z oświetleniem jest w dużo mniejszym stopniu zależna od złożoności geometrii sceny Tym samym możliwość wprowadzenia znacznie większej liczby źródeł światła Wady Deferred Shading: Problemy z technikami anti-aliasingu Właściwy kolor pikseli jest obliczany dopiero na samym końcu, w obrazie o docelowej rozdzielczości Większe zapotrzebowanie na pamięć Bufor klatki musi przechować wszystkie wartości wyliczone podczas pierwszego etapu jako tzw. G-Buffer Źródła obrazów: http://gamedev.ru http://richdavison.co.uk/ 18
Deferred Lighting Modyfikacją techniki Deferred Shading jest podejście nazywane Deferred Lighting Nazywane często Light pre-pass Odroczone renderowanie pełnoekranowej light mapy, która później jest używana do oświetlenia geometrii Trzy przebiegi Geometry pass Wejście: Geometria Wyjście: G-Buffer Lighting pass Wejście: G-Buffer Wyjście: Full-screen light map Final pass Wejście: Geometria + Full-screen light map Wyjście: Gotowa klatka Źródło obrazu: Azkanan, Leadwerks Software 19
Deferred Lighting Modyfikacją techniki Deferred Shading jest podejście nazywane Deferred Lighting Redukcja ilości pamięci potrzebnej do przechowania wyjścia z pierwszego etapu Źródło obrazu: Azkanan, Leadwerks Software 20
Deferred Lighting Przykładowa pełnoekranowa mapa światła. Źródło obrazu: Sony, Guerilla Games 21
Subsurface Scattering Technika subsurface scattering (SSS) polega na symulowaniu podpowierzchniowego rozpraszania światła Bierze pod uwagę światło, które przeszło (nie zostało odbite) przez częściowo przezroczystą powierzchnię Światło jest rozpraszane w głębi obiektu w wielu kierunkach Promienie światła wychodzą w innych miejscach powierzchni obiektu oraz pod innym kątem niż gdyby zostały odbite przez tę powierzchnię Odbicie powierzchniowe Rozproszenie podpowierzchniowe W rezultacie uwzględniona jest grubość przedmiotu, jak i zmiękczenie ostrych krawędzi cieni Materiały i obiekty takie jak: wosk, marmur, ludzka skóra, liście 22
Subsurface Scattering SSS ON Źródło obrazów: MrBluesummers SSS OFF 23
Subsurface Scattering W rzeczywistym świecie ludzka skóra odbija na powierzchni jedynie 6% światła, pozostałe 94% pochodzi z rozproszenia podpowierzchniowego! [Krishnaswamy, Baronoski 2004] Aby realistycznie renderować skórę, konieczne jest więc uwzględnienie SSS. Źródło obrazu: MrBluesummers 24
Subsurface Scattering Symulowanie SSS Podstawowe równanie renderingu obejmuje jedynie powierzchniowe odbicie światła opisane za pomocą BRDF Prosta metoda czasu rzeczywistego pozwalająca na przybliżenie wyglądu obiektu zbudowanego z np. wosku, polega na wyznaczeniu jego grubości w danym miejscu korzystając z bufora głębokości uzyskanego z punktu widzenia światła Odpowiednikiem BRDF uwzględniającym podpowierzchniowe rozproszenie jest BSSRDF Źródło obrazów: Wikipedia 25
Materiały wielowarstwowe Szczególnym przypadkiem powiązanym z SSS jest renderowanie materiałów wielowarstwowych (ang. layered material) Przykładowe zastosowanie: wizualizacja metalicznego lakieru samochodowego Przykład wyrenderowanego, kilkuwarstwowego materiału imitującego lakier samochodowy. Źródła obrazów: MrBluesummers D M Multimedia 26
Kaustyki Zjawisko polegające na trafianiu wielu odbitych lub załamanych fal światła w pobliżu jednego miejsca nazywamy kaustyką (ang. caustic) Najczęściej mamy do czynienia z takim zjawiskiem np. przy wklęsłych zwierciadłach i powierzchniach przepuszczających światło lub przy falujących powierzchniach płynów Szklanka wody powoduje załamanie fal światła w taki sposób, że powstają kaustyki Źródła obrazów: LuxRender Wikipedia 27
Kaustyki Realistyczne renderowanie polega na śledzeniu załamywanych lub odbijanych promieni światła Szczególnie dobrze widoczne przy technice mapowania fotonów (ang. photon mapping) Metody czasu rzeczywistego polegają m.in. na próbkowaniu powierzchni, operacjach per-fragment i nakładaniu tekstur imitujących obszary skupienia promieni Źródło obrazów: FXGuide.com, Mike Seymour. The State of Rendering - Part 2. 28
Definicja Geometrii Sposoby definicji geometrii. Backface culling. Renderowanie mapy wysokości. http://bazyluk.net/dydaktyka Gry komputerowe, Informatyka S1, II Rok
Renderowanie geometrii Wierzchołki Przetwarzanie wierzchołków Łączenie w prymitywy Geometria jest opisana przede wszystkim pozycjami wierzchołków Podczas procesu renderowania powierzchnie obiektów rysowane są za pomocą tzw. prymitywów Prymitywy to figury geometryczne odpowiadające za to, jak informacja o kolejnych pozycjach zostanie wykorzystana podczas rysowania Mogą to być np. punkty, linie, łamane, trójkąty, pasy trójkątów, czworokąty, wielokąty, itp. Rasteryzacja prymitywów Operacje na fragmentach Operacje na pikselach Bufor klatki Źródło obrazu: GPU Design 30
Renderowanie geometrii Prymitywy obsługiwane przez OpenGL: Źródło obrazu: http://www.opengl.org 31
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v1 v2 v0 v3 v4 v5 Myślimy o punktach w przestrzeni 3D, nie na płaszczyźnie 32
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v1 v2 v0 v3 v4 v5 Użycie prymitywu punkty (ang. points) 33
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v1 v2 v0 v3 v4 v5 Użycie prymitywu linie (ang. lines) 34
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v1 v2 v0 v3 v4 v5 Użycie prymitywu łamana (ang. line strip) 35
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v1 v2 v0 v3 v4 v5 Użycie prymitywu łamana zamknięta (ang. line loop) 36
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v1 v2 v0 v3 v4 v5 Użycie prymitywu trójkąty (ang. triangles) 37
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v2 v1 v0 v3 v4 v5 Użycie prymitywu wachlarz (ang. triangle fan) 38
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v2 v1 v0 v3 v4 v5 Użycie prymitywu pas trójkątów (ang. triangle strip) 39
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v2 v1 v0 v3 v4 v5 Dwa ostatnie wierzchołki nie są wykorzystane - aby powstał kolejny quad, potrzebne byłyby dwa następne! Użycie prymitywu czworokąty (ang. quads) 40
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v2 v1 v0 v3 v4 v5 Ten czworokąt jest niepoprawny ( zdegenerowany ) nie jest wypukły! Użycie prymitywu pas czworokątów (ang. quad strip) 41
Renderowanie geometrii Przykład: Mamy dane 6 wierzchołków o następujących położeniach: v2 v1 v3 v0 v4 v5 Ten wielokąt jest niepoprawny ( zdegenerowany ) nie jest wypukły! Użycie prymitywu wielokąt (ang. polygon) 42
Face culling Rozpatrzmy przypadek: scena zawiera sześcian Wierzchołki Przetwarzanie wierzchołków Łączenie w prymitywy Rasteryzacja prymitywów Operacje na fragmentach Operacje na pikselach Ile jego ścian jest widocznych na danej klatce animacji? Ile ścian jest renderowanych podczas rysowania klatki? W którym momencie potoku pomijane są tylne ściany? Bufor klatki 43
Face culling Technika face culling pozwala na pominięcie ścian widocznych z określonej strony jeszcze przed rozpoczęciem ich rasteryzacji O odrzuceniu decyduje kolejność wierzchołków po ich projekcji (ang. winding order) Zgodnie z ruchem wskazówek zegara (ang. clockwise) Przeciwnie do wskazówek zegara (ang. counter-clockwise) v0 Konieczne jest więc ustalenie w jakiej kolejności definiowane są wierzchołki ścian gdy patrzymy na ich przednią stronę, a potem konsekwentne trzymanie się ustalonej kolejności podczas opisywania geometrii obiektów. v3 CCW v1 v2 44
Face culling Jeśli po przejściu do współrzędnych kamery kolejność wierzchołków zgadza się z tą uznaną za przednią, to znaczy że widzimy przednią stronę ściany i należy ją poddać rasteryzacji Jeśli kolejność jest odwrotna, to widzimy tylną stronę ściany i możemy ją pominąć (jeśli używamy techniki back face culling) Źródło obrazu: http://www.learningopengl.com 45
Face culling Jak karta graficzna porównuje kolejność wierzchołków? Czy kąt pomędzy wektorem normalnym ściany, a wektorem obserwacji jest mniejszy lub równy 90 stopni? Wektor normalny obliczamy za pomocą iloczynu wektorowego trzech kolejnych wierzchołków w kolejności ich zdefiniowania Równoważne pytanie: Czy iloczyn skalarny między tymi wektorami jest większy lub równy zero? Jeśli tak, to kolejność jest różna od początkowej ścianę należy pominąć! N=(v 1 v 0 ) ( v 2 v 0 ) V 0 N 0 N V0 v0 v2-v0 v1-v0 v1 v2 46
Face culling Kiedy back face culling jest problematyczny? Gdy na scenie są przezroczyste obiekty Gdy występują obiekty o zerowej grubości, w których obie strony ścian są widoczne Do czego dodatkowo może służyć back face culling? Wówczas pomijanie tylnych ścian może być widoczne Usuwanie "niewidocznych" linii w renderowaniu wireframe, pod warunkiem że geometria jest wypukła Do czego mógłby służyć front face culling? Ukrywanie przednich ścian może być przydatne podczas renderowania mapy cieni, by uniknąć efektu self-shadowing gdy sprawdzane jest przysłonięcie sceny z punktu widzenia źródła światła 47
Face culling Jak korzystać z face cullingu w OpenGL? Włączanie: glenable(gl_cull_face); Wybór kolejności definicji przednich ścian: glfrontface(gl_ccw); // przeciwnie do ruchu wskazówek zegara glfrontface(gl_cw); // zgodnie z ruchem wskazówek zegara dotyczy wierzchołków definiowanych po wywołaniu tej funkcji Wybór ścian, które mają być pomijane: glcullface(gl_back); glcullface(gl_front); glcullface(gl_front_and_back); 48
Definicja geometrii w OpenGL Najprostszą metodą definicji i renderowania geometrii jest tzw. immediate mode Definicja własności wszystkich wierzchołków w każdej klatce Podejście którego współcześnie się już nie stosuje Jest nieefektywne W każdej klatce duża ilość danych przekazywana do karty graficznej Współcześnie używa się buforów w pamięci karty graficznej Jest najłatwiejsze w zrozumieniu Prymityw glbegin(gl_quads); glvertex3f(-1.0f, -1.0f, 0.0f); glvertex3f(1.0f, -1.0f, 0.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(-1.0f, 1.0f, 0.0f); glend(); 49
Kolor w OpenGL Atrybuty wierzchołków, np. ich kolor, określamy przed stworzeniem danego wierzchołka glcolor3f(1.0f, 0.0f, 0.0f); glbegin(gl_quads); glvertex3f(-1.0f, -1.0f, 0.0f); glvertex3f(1.0f, -1.0f, 0.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(-1.0f, 1.0f, 0.0f); glend(); glbegin(gl_quads); glcolor3f(0.0f, 0.0f, 0.0f); glvertex3f(-1.0f, -1.0f, 0.0f); glcolor3f(1.0f, 0.0f, 0.0f); glvertex3f(1.0f, -1.0f, 0.0f); glcolor3f(0.0f, 1.0f, 0.0f); glvertex3f(1.0f, 1.0f, 0.0f); glcolor3f(0.0f, 0.0f, 1.0f); glvertex3f(-1.0f, 1.0f, 0.0f); glend(); 50
DEFINICJA GEOMETRII Przykłady będą dotyczyły prostego sześcianu: y Przykładowe dane v4 v7 v0 v3 x v5 v6 v1 v2 z Dla uproszczenia na razie pomijamy wszystko poza pozycjami wierzchołków 51
DEFINICJA GEOMETRII Pozycje wierzchołków: y Przykładowe dane v4 = (0,1,0) v0 = (0,1,1) v7 = (1,1,0) v3 = (1,1,1) x v5 = (0,0,0) v1 = (0,0,1) v6 = (1,0,0) v2 = (1,0,1) z 52
DEFINICJA GEOMETRII Kolejność definicji ścian naszego sześcianu: Zakładamy użycie prymitywu GL_QUADS Przykładowe dane v4 y v7 v0 v3 x v1 z v5 v6 v2 Oczywiście możliwe będzie zyskanie na wydajności np. używając GL_QUAD_STRIP, ale chcemy prosty przypadek GL_QUADS nie jest dostępny we współczesnych wersjach OpenGL, ale pozwala łatwiej zobrazować zagadnienie 53
DEFINICJA GEOMETRII Kolejność definicji wierzchołków: Przykładowe dane v0 -> v1 -> v2 -> v3 v3 -> v2 -> v6 -> v7 v7 -> v6 -> v5 -> v4 v4 -> v5 -> v1 -> v0 v0 -> v3 -> v7 -> v4 v1 -> v5 -> v6 -> v2 Czyli: 24 zestawy po 3 współrzędne po 4 bajty v4 y v0 Pamiętamy o backface cullingu v7 24 3 4 = 288 bajtów zakładamy że front-face'y definiowane są przeciwnie do ruchu wskazówek zegara (CCW) v3 x v1 z v5 v6 v2 54
IMMEDIATE MODE Ogólna charakterystyka Najprostsze podejście polegające na bezpośredniej definicji geometrii Wartości (np. współrzędne wierzchołków) dla każdorazowego żądania renderowania, przekazywane są do serwera od nowa v4 Oznacza to ogromną ilość danych przesyłaną co klatkę od klienta do serwera Oznacza to ogromną liczbę wywołań funkcji OpenGL y v7 v0 v3 x v1 z v5 v6 v2 55
IMMEDIATE MODE Sposób użycia Otwarcie bloku definicji geometrii Definicja geometrii v4 Wierzchołki, wektory normalne, współrzędne tekstur... Zamknięcie bloku y Wskazanie pożądanego prymitywu Zlecenie renderowania, ewentualne buforowanie Przykład dla pierwszego quada: v7 v0 glbegin(gl_quads); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(1.0f, 0.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glend(); v3 x v1 z v5 v6 v2 56
IMMEDIATE MODE Przykład użycia Dla całego sześcianu: glbegin(gl_quads); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(1.0f, 0.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 0.0f, 1.0f); glvertex3f(1.0f, 0.0f, 0.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(1.0f, 0.0f, 0.0f); glvertex3f(0.0f, 0.0f, 0.0f); glvertex3f(0.0f, 1.0f, 0.0f); v4 v7 z Nie zmieniają się po co? Powtarzające się wierzchołki wysyłane są kilkukrotnie Już zostały przesłane po co? glvertex3f(0.0f, 1.0f, 0.0f); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 0.0f); v3 glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(0.0f, 1.0f, 0.0f); x v1 glvertex3f(0.0f, 1.0f, 0.0f); glvertex3f(0.0f, 0.0f, 0.0f); glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(0.0f, 1.0f, 1.0f); y v0 W każdej klatce wysyłamy do serwera pozycje wszystkich wierzchołków v5 v6 glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(0.0f, 0.0f, 0.0f); glvertex3f(1.0f, 0.0f, 0.0f); glvertex3f(1.0f, 0.0f, 1.0f); v2 glend(); 57
DISPLAY LIST Ogólna charakterystyka Skompilowana sekwencja instrukcji zmieniających stan serwera Przechowywana w pamięci karty graficznej Raz utworzona nie może być modyfikowana v4 y v7 v0 Nie nadaje się do animacji bryły Sterownik odpowiada za to, by lista została wykonana efektywnie Każda lista ma przypisany identyfikator, który służy jej późniejszemu wywołaniu (żądaniu renderowania) v3 x v1 z v5 v6 v2 58
DISPLAY LIST Sposób użycia Utworzenie display listy Kompilacja display listy Wierzchołki, wektory normalne, współrzędne tekstur... Można dołączyć inne zmiany stanu serwera Wywołanie display listy v4 y Żądanie identyfikatora Żądanie renderowania z użyciem identyfikatora Przykład dla pierwszego quada: v7 v0 GLuint id = glgenlists(1); glnewlist(id, GL_COMPILE); glbegin(gl_quads); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(1.0f, 0.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glend(); glendlist(); // Renderowanie: glcalllist(id); v3 x v1 z v5 v6 v2 59
DISPLAY LIST Przykład użycia v4 y v7 v0 v3 x v1 z v5 v6 v2 Dla całego sześcianu: GLuint id = glgenlists(1); glnewlist(id, GL_COMPILE); glbegin(gl_quads); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(1.0f, 0.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 0.0f, 1.0f); glvertex3f(1.0f, 0.0f, 0.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(1.0f, 0.0f, 0.0f); glvertex3f(0.0f, 0.0f, 0.0f); glvertex3f(0.0f, 1.0f, 0.0f); glvertex3f(0.0f, 1.0f, 0.0f); glvertex3f(0.0f, 0.0f, 0.0f); glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(0.0f, 1.0f, 0.0f); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(0.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 1.0f); glvertex3f(1.0f, 1.0f, 0.0f); glvertex3f(0.0f, 1.0f, 0.0f); glvertex3f(0.0f, 0.0f, 1.0f); glvertex3f(0.0f, 0.0f, 0.0f); glvertex3f(1.0f, 0.0f, 0.0f); glvertex3f(1.0f, 0.0f, 1.0f); glend(); glendlist(); // Pozycje wszystkich wierzchołków wysyłamy tylko raz Podczas renderowania kolejnych klatek korzystamy z danych znajdujących się w pamięci graficznej Możliwości list wykraczają poza zwykłą definicję geometrii Znacząco zyskuje na tym wydajność Możliwość użycia innych instrukcji zmieniających stan Nie ma możliwości modyfikacji listy po jej skompilowaniu // Dla każdej klatki: glcalllist(id); 60
VERTEX ARRAY Ogólna charakterystyka Ciągła tablica po stronie klienta (w pamięci głównej) zawierająca dane wierzchołków Możliwość dowolnej manipulacji zawartości Możliwość wybiórczego renderowania v4 y v7 v0 Rozwiązuje problem redefinicji powtarzających się wierzchołków, co daje oszczędność pamięci Interleaved arrays, czyli łączone tablice dla różnych danych per-vertex Np. pozycja, wektor normalny, kolor, współrzędne tekstury... Wszystko w jednej tablicy (co ma i zalety, i wady) v3 x v1 z v5 v6 v2 61
VERTEX ARRAY Sposób użycia Utworzenie tablicy z wartościami Wskazanie serwerowi miejsca w pamięci, gdzie znajdują się dane Przekazanie adresu tablicy Żądanie renderowania v4 Zwyczajna tablica po stronie klienta Można określić, które z danych i w jakiej kolejności nas interesują y v7 v0 v3 x v1 z v5 v6 v2 62
VERTEX ARRAY Przykład dla pierwszego quada: Sposób użycia (c.d.) GLfloat buffer[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; // Renderowanie: glenableclientstate(gl_vertex_array); glvertexpointer(3, GL_FLOAT, 0, buffer); v4 y gldrawarrays(gl_quads, 0, 4); v7 v0 gldisableclientstate(gl_vertex_array); v3 x v1 z v5 v6 v2 Aby wskazać gdzie przechowywane są wartości innych atrybutów, można posłużyć się funkcjami: glnormalpointer(), gltexcoordpointer(), glvertexattribpointer(),... 63
VERTEX ARRAY Wybiórcze renderowanie Wybiórcze renderowanie można zrealizować na kilka różnych sposobów: Podzakres tablicy od-do: v4 y v7 v0 v3 x v1 z v5 v6 v2 gldrawarrays(primitive, from, to) Wskazanie indeksów wierzchołków: gldrawelements(primitive, num_indices, index_type, indices) Dzięki temu nie musimy redefiniować wierzchołków! Przykład cały sześcian: GLfloat buffer[] = { 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f }; GLubyte indices[] = { 0, 1, 2, 3, 3, 2, 6, 7, 7, 6, 5, 4, 4, 5, 1, 0, 0, 3, 7, 4, 1, 5, 6, 2 }; // Renderowanie: glenableclientstate(gl_vertex_array); glvertexpointer(3, GL_FLOAT, 0, buffer); gldrawelements(gl_quads, 24, GL_UNSIGNED_BYTE, indices); gldisableclientstate(gl_vertex_array); 64
VERTEX ARRAY Wybiórcze renderowanie v4 Wybiórcze renderowanie można zrealizować na kilka różnych sposobów (c.d.): v7 gldrawrangeelements(primitive, start, end, num_indices, index_type, indices) start i end określają zakres wartości elementów z tablicy indeksów (nie zakres tablicy, który zostanie użyty!) Służy to do małej optymalizacji OpenGL będzie wiedział, jakiego zakresu indeksów się spodziewać bez konieczności iterowania po tablicy indeksów. W ten sposób może lepiej zaplanować odczyt z VA. v3 Co cechuje Vertex Arrays? x z y v0 v1 Wskazanie indeksów wierzchołków oraz ich zakresu: v5 v6 Możliwość dowolnej kontroli wartości w trakcie renderowania Przydatne przy animacji bryły Konieczność przesłania tablicy do pamięci karty graficznej Dużo mniej odwołań do funkcji OpenGL v2 65
WYBIÓRCZE RENDEROWANIE Przykład zastosowania Inny przykład praktycznego wykorzystania selektywnego renderowania geometrii z użyciem indeksów wierzchołków: v4 Level of detail (LOD) dla map wysokości y v7 v0 v3 x v1 z v5 v6 v2 Źródło obrazu: http://windows-tech.info 66
VERTEX ARRAY Dodatkowe atrybuty Przekazywanie wartości dodatkowych atrybutów wierzchołków v4 y v7 v0 Nie tylko pozycja (współrzędne) jest wartością per-vertex Wektory normalne, współrzędne tekstur, wektory styczne, kolor, stopień zniszczenia, wiek, stopień deformacji, zabrudzenie, następna pozycja,... Możemy definiować własne atrybuty, które wykorzystamy w naszych shaderach Są dwa podejścia rozwiązania tej kwestii: Oddzielne tablice dla każdego z atrybutów Jedna tablica zawierająca przeplatające się wartości atrybutów (tzw. interleaved arrays) v3 x v1 z v5 v6 v2 67
INTERLEAVED ARRAYS Interleaved arrays przykład: Sposób działania Współrzędne pozycji (p) Wektor normalny (n) Współrzędne 2D tekstury (t)... v4 v0 y v7 v0 v3 x v1 z v5 v6 v2 v1 v2 Parametry każdego atrybutu: offset odległość w bajtach od początku wierzchołka stride odległość w bajtach pomiędzy wierzchołkami Zakładając, że szerokość każdej wartości to 4B, mamy: offset(p) = 0, stride(p) = 32 offset(n) = 12, stride(n) = 32 offset(t) = 24, stride(t) = 32 68
INTERLEAVED ARRAYS Sposób użycia Reprezentacja interleaved array w pamięci głównej z punktu widzenia programu v4 v7 SVertex buffer[]; v3 x v1 z C++ gwarantuje, że struktura zajmuje ciągły obszar pamięci, a składowe mają zachowaną kolejność typedef struct { GLfloat pos[3]; GLfloat normal[3]; GLfloat tex[2]; } SVertex; y v0 Wygodnym podejściem jest użycie struktur v5 glvertexpointer(3, GL_FLOAT, sizeof(svertex), buffer); glnormalpointer(gl_float, sizeof(svertex), buffer + 3 * sizeof(glfloat)); gltexcoordpointer(2, GL_FLOAT, sizeof(svertex), buffer + 6 * sizeof(glfloat)); v6 v2 Zamiast arytmetyki wskaźników (mogącej doprowadzić do problemów), warto wykorzystać po prostu: &buffer[0].pos, &buffer[0].normal, &buffer[0].tex 69
INTERLEAVED ARRAYS Interleaved arrays dodatkowe informacje: Dodatkowe informacje Jeśli np. planujemy często zmieniać pozycję wierzchołków, pozostawiając pozostałe atrybuty bez zmian, warto użyć oddzielnych tablic Warto dążyć do tego, by stride był równy wielokrotności 32. Wiele kart graficznych potrafi wtedy lepiej organizować odczyt z pamięci v4 y v7 v0 v3 x v1 z v5 Nawet kosztem zwiększonego zużycia pamięci, wprowadzając padding! Możemy określić stride jako 0, jeśli nasze dane są ciasno upakowane (żadnego paddingu) i tablice zawierają wartości tylko pojedynczych atrybutów Jeśli nasz układ danych jest standardowy, można użyć funkcji glinterleavedarrays() w celu wyboru tego układu (oszczędza liczbę wywołań funkcji OpenGL) v6 v2 70
VBO Ogólna charakterystyka v4 y Vertex Buffer Object (VBO) technika pozwalająca na łatwe manipulowanie zawartością pamięci karty graficznej Siostrzana technika do Frame Buffer Object (FBO) i Pixel Buffer Object (PBO) Pozwala zarezerwować, wypełnić, odczytać, modyfikować zawartość bufora Po stronie klienta przechowywany jest jedynie identyfikator bufora v7 v0 v3 Klient x v1 z v5 Serwer id v6 VBO v2 (dane) 71
VBO Sposób użycia Wygenerowanie nowego bufora Bindowanie bufora Wybór bufora, który ma stać się aktywny Określenie rodzaju bufora (GL_ARRAY_BUFFER) Alokacja bufora v4 y v7 v3 Określenie charakteru danych: czy będą często modyfikowane, czy też nie... v5 v6 v2 Wpływa na optymalny przydział pamięci Wskazanie miejsc w pamięci x z Przekazanie danych do pamięci karty graficznej v0 v1 Otrzymujemy id bufora Nie przekazujemy wskaźników (bo ich nie mamy), tylko określamy liczbowo offset i stride dla VBO Żądanie renderowania Można określić, które z danych i w jakiej kolejności nas interesują 72
VBO Przykład użycia Przykład dla pierwszego quada: GLfloat buffer[] = {0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; GLuint vbo; glgenbuffers(1, &vbo); glbindbuffer(gl_array_buffer, vbo); glbufferdata(gl_array_buffer, sizeof(buffer), buffer, GL_STATIC_DRAW); delete[] buffer; // Oczywiście jest to błędne użycie (alokacja podczas kompilacji) glbindbuffer(gl_array_buffer, 0); v4 // Renderowanie: glbindbuffer(gl_array_buffer, vbo); y v7 v0 glenableclientstate(gl_vertex_array); glvertexpointer(3, GL_FLOAT, 0, 0); v3 gldrawarrays(gl_quads, 0, 4); x v1 z v5 v6 v2 gldisableclientstate(gl_vertex_array); glbindbuffer(gl_array_buffer, 0); // Sprzątanie: gldeletebuffers(1, &vbo); 73
VBO Modyfikacja zawartości Istnieje możliwość modyfikacji zawartości VBO: Ponowna alokacja pamięci i wysłanie danych v4 y v7 v0 v1 z Poprzez zastąpienie części danych glbuffersubdata(target, offset, size, data) Pamięć nie jest realokowana! Warto użyć nawet, jeśli zamieniamy całą zawartość bufora. Poprzez mapowanie pamięci glmapbuffer(target, access) glunmapbuffer(target) Otrzymujemy wskaźnik do pamięci, możemy dowolnie ją czytać i zmieniać Kosztowna synchronizacja z GPU v3 x v5 v6 v2 glbufferdata(target, size, data, usage) Warto zastanowić się nad wydajnością i synchronizacją Wykorzystanie techniki podwójnego buforowania (dwa VBO, które zamieniamy miejscami rysujemy z jednego, drugi uaktualniamy) może rozwiązać problem synchronizacji 74
IBO Indeksowanie VBO Wybiórcze renderowanie z VBO jest jeszcze lepsze v4 Mamy możliwość stworzenia bufora indeksów: Index Buffer Object (IBO) IBO działa analogicznie do VBO: Musimy go stworzyć, wybrać, zaalokować, wypełnić, zwolnić GL_ELEMENT_ARRAY_BUFFER IBO jest przechowywany w pamięci karty graficznej odczyt podczas renderowania nie wymaga przesyłania danych z pamięci głównej! y v7 v0 v3 x v1 z v5 v6 v2 75
IBO Przykład użycia Przykład cały sześcian: GLfloat buffer[] = { 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f }; GLubyte indices[] = { 0, 1, 2, 3, 3, 2, 6, 7, 7, 6, 5, 4, 4, 5, 1, 0, 0, 3, 7, 4, 1, 5, 6, 2 }; GLuint vbo, ibo; glgenbuffers(1, &vbo); glbindbuffer(gl_array_buffer, vbo); glbufferdata(gl_array_buffer, sizeof(buffer), buffer, GL_STATIC_DRAW); delete[] buffer; v4 y glgenbuffers(1, &ibo); glbindbuffer(gl_element_array_buffer, ibo); glbufferdata(gl_element_array_buffer, sizeof(indices), indices, GL_STATIC_DRAW); delete[] indices; v7 v0 v3 x v1 z v5 v6 v2 // Renderowanie: glenableclientstate(gl_vertex_array); glvertexpointer(3, GL_FLOAT, 0, buffer); gldrawelements(gl_quads, 24, GL_UNSIGNED_BYTE, 0); gldisableclientstate(gl_vertex_array); // Sprzątanie: glbindbuffer(gl_array_buffer, 0); glbindbuffer(gl_element_array_buffer, 0); gldeletebuffers(1, &vbo); gldeletebuffers(1, &ibo); 76
Renderowanie mapy wysokości Zadanie: zwizualizować rzeźbę terenu daną mapą wysokości (heightmapą) Bitmapa, gdzie wartość każdego piksela jest zapisem wysokości terenu w danym miejscu Odcienie szarości Regularna siatka punktów pomiarowych Na jej podstawie należy zbudować zbiór wierzchołków, wyrenderować go i ocieniować dla uzyskania jak najlepszego efektu. Ilość wykorzystanej pamięci karty graficznej powinna być jak najmniejsza. Mapa wysokości Czyli dane wejściowe naszego programu. Jaśniejszy kolor na mapie oznacza większą wysokość. 77
Renderowanie mapy wysokości Chmura punktów Każdy punkt pomiarowy zwizualizowany jako punkt. 78
Renderowanie mapy wysokości Powierzchnia Teren wyrenderowany jako powierzchnia złożona z trójkątów. Kolor fragmentu odpowiada wysokości terenu w danym miejscu. 79
Renderowanie mapy wysokości Oświetlenie Obliczone wektory normalne, zaimplementowany model oświetlenia Phonga. 80
Renderowanie mapy wysokości Teksturowanie Proceduralny dobór tekstury na podstawie wysokości oraz nachylenia terenu w danym fragmencie. 81
Renderowanie mapy wysokości Wierzchołki: VBO Zawiera informacje o wysokościach, odczytane z mapy Każdy wierzchołek występuje tylko raz Dodatkowo wektory normalne, współrzędne tekstury Indeksy: IBO Za pomocą indeksów wierzchołków z VBO tworzy siatkę która posłuży do wyrenderowania powierzchni Żądanie renderowania gldrawelements() Prymityw GL_TRIANGLE_STRIP Najlepiej zawrzeć całą mapę w tylko jednym żądaniu! 82
Renderowanie mapy wysokości 1 Jeden render call Używając prymitywu TRIANGLE_STRIP, mamy możliwość prostego zawarcia całego terenu w jednym zbiorze wierzchołków. 83
Renderowanie mapy wysokości 1 Jeden render call W każdej iteracji po kolumnach mapy wysokości, w obrębie jednego wiersza, będziemy dodawać do zbioru dwa wierzchołki: 2 - z obecnego wiersza - z kolejnego wiersza 84
Renderowanie mapy wysokości 1 3 Jeden render call W każdej iteracji po kolumnach mapy wysokości, w obrębie jednego wiersza, będziemy dodawać do zbioru dwa wierzchołki: 2 - z obecnego wiersza - z kolejnego wiersza 85
Renderowanie mapy wysokości 1 3 2 4 Jeden render call W każdej iteracji po kolumnach mapy wysokości, w obrębie jednego wiersza, będziemy dodawać do zbioru dwa wierzchołki: - z obecnego wiersza - z kolejnego wiersza 86
Renderowanie mapy wysokości 1 3 5 7 2 4 6 8 Jeden render call Problem pojawia się gdy dotrzemy do końca wiersza. Jak przejść do następnego wiersza nie przerywając renderowania? 87
Renderowanie mapy wysokości 1 3 5 7 2 4 6 8 Jeden render call Jeśli zaczęlibyśmy od razu kolejny wiersz w taki sam sposób jak poprzednio, otrzymalibyśmy niepożądany trójkąt przecinający nasz misternie przygotowywany teren. 9 88
Renderowanie mapy wysokości 1 3 5 7 2 4 6 8 9 Jeden render call Powszechnie wykorzystywanym sposobem jest użycie tzw. zdegenerowanych trójkątów. Dodajemy jeszcze jeden wierzchołek w tym samym miejscu. Stosunkowo małym kosztem tworzymy w ten sposób trójkąt, który podczas rasteryzacji nie wytworzy żadnego fragmentu. 89
Renderowanie mapy wysokości 1 3 5 7 2 10 4 6 8 9 Jeden render call Przechodząc teraz do nowego wiersza, tworzymy jeszcze jeden zdegenerowany trójkąt. Uwaga: używając prymitywu LINE_STRIP, uzyskamy tutaj dodatkową linię przechodzącą przez całą mapę! 90
Renderowanie mapy wysokości 1 3 5 7 4 6 8 9 Jeden render call Gdybyśmy od razu zaczęli kolejny wiersz, powstałby zbędny trójkąt 9-10-11. 2 10 11 Dlatego potrzebujemy jeszcze jednego zdegenerowanego trójkąta, by móc swobodnie iterować po kolejnym wierszu. 91
Renderowanie mapy wysokości 1 3 5 7 4 6 8 9 Jeden render call Teraz możemy już swobodnie zająć się kolejnym wierszem. Całość można rozwiązać też na inne sposoby, np. odwracając kolejność co drugiego wiersza. 2 10 11 12 92
Renderowanie mapy wysokości 1 3 5 7 4 6 8 9 Jeden render call Teraz możemy już swobodnie zająć się kolejnym wierszem. Całość można rozwiązać też na inne sposoby, np. odwracając kolejność co drugiego wiersza. 2 10 11 13 12 93
Renderowanie mapy wysokości 1 3 5 7 4 6 8 9 Jeden render call Teraz możemy już swobodnie zająć się kolejnym wierszem. Całość można rozwiązać też na inne sposoby, np. odwracając kolejność co drugiego wiersza. 2 10 11 12 13 14 94
Renderowanie mapy wysokości 1 3 5 7 2 10 11 4 6 8 9 17 12 20 21 14 22 24 Jeden render call Postępujemy w ten sposób do samego końca. 13 15 W ten sposób uzyskujemy całą powierzchnię mapy. 16 23 18 25 26 19 27 28 95
Renderowanie mapy wysokości Wektory normalne Musimy dla każdego wierzchołka obliczyć współrzędne wektora normalnego. Pamiętajmy, że jeśli zależy nam na uzyskaniu gładkiej powierzchni, wektory normalne powinny być identyczne dla wierzchołków o tej samej pozycji. Dlatego wektory normalne należy powiązać z punktami węzłowymi mapy wysokości, analogicznie do wysokości terenu w danym miejscu. Źródło obrazu: http://www.lighthouse3d.com 96
Renderowanie mapy wysokości Wektory normalne Jednym ze sposobów jest uśrednienie wektorów normalnych obliczonych dla wszystkich zbiegających się w danym wierzchołku ścian. To nie muszą być dokładnie te ściany, które faktycznie renderujemy! Warto uwzględnić wszystkie trójkąty wokoło rozpatrywanego w danej chwili wierzchołka. Źródło obrazu: http://www.lighthouse3d.com 97
Renderowanie mapy wysokości Wektory normalne v (i - 1,j - 1 ) By obliczyć wektor normalny pojedynczego trójkąta, należy wyliczyć iloczyn wektorowy wektorów zbudowanych z jego boków. v (i - 1,j ) Budując wektory, bierzemy pod uwagę wszystkie trzy współrzędne wierzchołków trójkąta wszak zależy nam na uwzględnieniu nachylenia terenu w trzech wymiarach. n0 =v (i 1, j 1) v (i 1, j) Źródło obrazu: http://www.lighthouse3d.com 98
Renderowanie mapy wysokości Wektory normalne Należy pamiętać o kolejności mnożonych wektorów, która wpłynie na zwrot wyniku ( reguła śruby prawoskrętnej, reguła prawej dłoni ). v (i - 1,j ) v (i - 1,j +1 ) n1=v (i 1, j) v (i 1, j+ 1) Źródło obrazu: http://www.lighthouse3d.com 99
Renderowanie mapy wysokości Wektory normalne Postępujemy tak samo dla wszystkich ośmiu trójkątów. v ( i- 1, j +1 ) v ( i, j +1) n2 =v(i 1, j+1) v (i, j+1) Źródło obrazu: http://www.lighthouse3d.com 100
Renderowanie mapy wysokości Wektory normalne Na końcu sumujemy wektory, które obliczyliśmy dla pojedynczych trójkątów. Wynik dzielimy przez ich liczbę, aby nasz wektor normalny miał długość 1. Potem przechodzimy do kolejnego wierzchołka i powtarzamy proces. Zamiast trójkątów, można rozpatrywać czworokąty. Wynikowe wektory normalne można poddać dodatkowo rozmyciu z użyciem filtracji splotowej. 8 1 N = ni 8 i=0 Źródło obrazu: http://www.lighthouse3d.com 101
Oświetlenie Zaawansowane techniki oświetlenia, c.d. http://bazyluk.net/dydaktyka Gry komputerowe, Informatyka N1, III Rok