Grafika Komputerowa 3D
Transkrypt
Grafika Komputerowa 3D
Grafika Komputerowa 3D Etap drugi Należy wybrać i zrealizować w scenie z etapu pierwszego dowolny zestaw z podanych poniżej efektów tak, aby zgromadzić odpowiednią liczbę punktów. Nie można za drugi etap otrzymać więcej niż 20 punktów, ale nadmiar punktów przechodzi na kolejny etap. Poszczególne efekty nie muszą ze sobą współpracować, a zatem włączenie jednego efektu może powodować wyłączenie innych, jeśli to ułatwi implementację. Uwaga dla piszących w C/C++ z użyciem OpenGL: W nagłówku gl.h znajdują się tylko deklaracje dotyczące jednej z pierwszych wersji standardu. Z późniejszych rozszerzeń wygodnie jest korzystać przy pomocy OpenGL SDK dostępnego pod adresem http://oss.sgi.com/projects/ogl-sample/GLsdk.zip albo The OpenGL Extension Wrangler Library dostępnego pod adresem http://glew.sourceforge.net/ . Ostateczny termin oddania projektu to 12 grudnia. W razie pytań dotyczących szczegółów realizacji poszczególnych zadań zapraszam na laboratoria we wtorki 10:15-12:00 w 317. 1. Filtrowanie tekstur Wprowadzenie Teksturowanie w ogólności polega na wyznaczeniu przekształcenia odwzorowującego piksele na ekranie w obszar obrazu tekstury (zbiór tekseli). Poza przypadkami szczególnymi wyznaczony obszar tekstury rzadko pokrywa się z pojedynczym tekselem. W celu uzyskania atrakcyjnego efektu na ekranie, wylicza się kolor piksela jako średnią ważoną kilku tekseli. Filtr powiększenia stosowany jest, gdy obraz piksela jest mniejszy od rozmiaru teksela. Do wyboru mamy dwa rozwiązania: najbliższy teksel środka piksela (czyli brak filtrowania) oraz dwuliniową interpolację pomiędzy kolorami czterech sąsiednich tekseli. Filtr pomniejszenia stosowany jest, gdy obraz piksela jest większy od rozmiaru teksela. W takim przypadku zwykle używa się mipmap, czyli ciągu coraz mniejszych tekstur (poziomów mipmap), w którym pojedynczy teksel kolejnej tekstury jest średnią wartością czwórki tekseli poprzedniej tekstury. Filtrowanie przy pomniejszeniu może polegać na: wyborze najbliższego teksel środka piksela, dwuliniowej interpolacji pomiędzy kolorami czterech sąsiednich tekseli odpowiedniego poziomu mipmap, trzyliniowej interpolacji mipmap, na którą składa się wykonanie dwuliniowej interpolacji w dwóch sąsiednich poziomach mipmap i wykonanie liniowej interpolacji tak uzyskanych kolorów. Zadania A) Wybierz przynajmniej jeden obiekt w scenie. Upewnij się, że ma on przypisane współrzędne tekstury. Nałóż teksturę i włącz liniowy filtr dla powiększenia. [2 pkt] [+2 pkt] – za renderowanie „skyboxa”, czyli wewnętrznej strony ścian sześcianu otaczającego scenę, z nałożoną na niego mapą sześcienną („cubemap texture”) zamiast zwyczajnego zestawu tekstur dwuwymiarowych; sześcian nie powinien podlegać wpływowi świateł w scenie, a jego tekstura powinna mieć włączone filtrowanie liniowe dla powiększenia B) Nałóż na wybrany obiekt w scenie teksturę z wygenerowanymi poziomami mipmap i użyj filtrowania trzyliniowego mipmap. [2 pkt] – wersja podstawowa [+1 pkt] – za dodanie interfejsu umożliwiającego zmianę poziomu mipmap wybieranego podczas teksturowania przez DirectX lub OpenGL, co w efekcie powoduje rozmycie lub wyostrzenie obrazu na powierzchni obiektu (służy do tego instrukcja glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, ...) w OpenGL lub ustawienie device.SamplerState[i].MipMapLevelOfDetailBias w DirectX Managed) 2. Automatyczne generowanie i przekształcenia współrzędnych tekstury Wprowadzenie DirectX oraz OpenGL umożliwiają automatyczne generowanie współrzędnych tekstury na podstawie położenia wierzchołka w scenie lub w układzie kamery. Dodatkowo istnieje możliwość zdefiniowania macierzy przekształcenia dla współrzędnych tekstury. Można wyróżnić dwa zastosowania automatycznego generowania współrzędnych tekstury: rzutowanie równoległe lub perspektywiczne obrazu na powierzchnię siatek oraz mapowanie środowiska. W pierwszym przypadku postępujemy podobnie jak podczas rzutowania geometrii do widoku z kamery - mnożymy pozycje wierzchołków przez zadaną macierz, ale tym razem wynik traktowany jest jako współrzędne tekstury, a nie pozycja na ekranie. Jeśli przykładowo potraktujemy lampę jak drugą kamerę w scenie, w wyniku takiego rzutowania otrzymamy współrzędne wierzchołków siatki trójwymiarowego obiektu w układzie lampy. Jeżeli podczas teksturowania obiektu wykorzystamy te właśnie współrzędne tekstury efektem będzie projekcja pewnego obrazu rzucanego przez lampę na powierzchnię obiektu. Podczas mapowania środowiska najlepiej jest wykorzystać mapę sześcienną. Mapa sześcienna to szóstka dwuwymiarowych tekstur przypisanych poszczególnym ścianom sześcianu. Współrzędne tekstury dla mapy sześciennej to trójka liczb, które wyznaczają kierunek promienia wychodzącego ze środka takiego sześcianu. Teksel pobrany dla danych współrzędnych tekstury odpowiada przecięciu tego promienia z powierzchnią sześcianu. Mapowanie środowiska uzyskamy, jeśli wykorzystamy jako współrzędne mapy sześciennej nakładanej na pewien obiekt, wektory odbicia promienia biegnącego od obserwatora względem płaszczyzny stycznej do powierzchni (wyznaczonej przez wersor normalny). W DirectX Managed do automatycznego generowania współrzędnych tekstury na podstawie współrzędnych wierzchołka siatki i ewentualnie normalnej w tym wierzchołku (przy mapowaniu środowiska) służą instrukcje postaci: device.TextureState[0].TextureCoordinateIndex = ...; device.TextureState[0].TextureTransform = ...; Do przekształcenia współrzędnych tekstury przypisanych do obiektu służy instrukcja postaci: device.SetTransform(TransformType.Texture0, ...); W OpenGL odpowiednie są to polecenia postaci: glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, ...); glTexGenfv(GL_S, GL_OBJECT_PLANE, ...); glEnable(GL_TEXTURE_GEN_S); glMatrixMode(GL_TEXTURE); Zadania A) Wykonaj rzutowanie perspektywiczne pewnego obrazu rzucanego przez światło reflektorowe (np. lampę) na stół i obiekty na nim. [5 pkt] B) Nałóż na blat stołu (lub inny obiekt sceny) teksturę poprzez wykorzystanie dwóch spośród trzech współrzędnych wierzchołków blatu stołu (lub innego obiektu sceny) do automatycznego wygenerowania współrzędnych tekstury. [2 pkt] C) Wykonaj mapowanie środowiska - renderuj odbicia mapy sześciennej w pewnym niepłaskim obiekcie sceny. [5 pkt] 3. Mieszanie alfa (przezroczystość) Wprowadzenie Mieszanie alfa polega na wykorzystaniu dodatkowej czwartej składowej koloru jako współczynnika w równaniu, które na podstawie koloru już zapisanego w buforze (destination color - dst_color, destination alpha - dst_alpha) oraz koloru właśnie rysowanego obiektu (source color - src_color, source alpha - src_alpha) obliczy nową wartość koloru w buforze. W przypadku przezroczystości odpowiedni wzór to src_alpha * src_color + (1-src_alpha) * dst_color. Mieszanie addytywne z kolei to wzór postaci src_alpha * src_color + dst_color. Trudna część realizacji przezroczystości związana jest z tym, że kiedy na ekranie nakłada się na siebie kilka warstw powierzchni przezroczystych nie można ich rysować w dowolnej kolejności. Aby otrzymać poprawny efekt trzeba je uporządkować według relacji zasłaniania. W przypadku mieszania addytywnego kolejność rysowania obiektów jest dowolna. W przypadku bryły wypukłej realizacja przezroczystości jest prosta: wystarczy wykorzystać „backface culling” aby przy dwukrotnym rysowaniu tej samej bryły w pierwszej kolejności narysować jej tylnią część, a następnie przednią. Jeśli mamy kilka brył, dla których kule opisane na nich są rozłączne, to możemy uporządkować je, sortując na podstawie odległości środków tych kul od kamery. Możemy też wykorzystać równanie płaszczyzny, która rozdziela obiekty. W zależności od tego po której stronie płaszczyzny będzie się znajdować w danym momencie kamera, będziemy wiedzieć, które obiekty należy narysować jako pierwsze. Zadania A) Narysuj poświatę na około obiektu. Po narysowaniu obiektu narysuj jeszcze jego powiększoną kopię, ale tym razem bez teksturowania, używając innego materiału (np. w kolorze czerwonym) i z włączonym mieszaniem addytywnym. [2 pkt] [+3 pkt] - za podświetlenie obiektu tylko po najechaniu na niego kursorem myszy B) Umieść w scenie kilka (minimum trzy) rozłącznych wypukłych brył (kul, walców, stożków, sześcianów) z nałożoną dowolną teksturą i przypisanym materiałem. Niech kanał alfa podczas ich rysowania będzie brany z materiału, a kolor niech będzie iloczynem koloru tekstury i koloru materiału. Zapewnij poprawne wyświetlanie przezroczystości (rysowanie powierzchni poszczególnych brył w kolejności od najdalszej do najbliższej). [6 pkt] 4. Multiteksturowanie Wprowadzenie Multiteksturowanie polega na obliczeniu koloru powierzchni obiektu na podstawie kolorów pobranych z więcej niż jednej tekstury jednocześnie. Obliczenie koloru zwykle przebiega w ten sposób, że na początku mieszany jest kolor uzyskany w wyniku obliczeń oświetlenia z kolorem pierwszej tekstury, a potem uzyskany kolor jest mieszany z kolorem kolejnej tekstury itd. Zadania A) Użyj przynajmniej dwóch różnych tekstur by uzyskać określony sposób pokolorowania powierzchni obiektu. [2 pkt] B) Kalkomania. Naklej pewien znak dany w postaci tekstury z kanałem alfa na powierzchnię (może być to powierzchnia płaska) oteksturowanego już obiektu. Zapewnij prosty interfejs do obracania, zmiany położenia i wielkości kalkomanii (należy w tym celu wykorzystać macierz przekształcenia współrzędnych tekstury omawianą w poprzednim punkcie). [+3 pkt] 5. Lustro Wprowadzenie Zrealizowanie poprawnego (nie przybliżonego) efektu płaskiego lustra wymaga wykonania trzech przebiegów rysowania geometrii: - najpierw rysowana jest oryginalna scena; - następnie rysowana jest powierzchnia płaskiego lustra (test bufora głębokości musi być w tym przebiegu ustawiony na mniejsze lub równe, jeśli rysujemy drugi raz te same piksele; przed rysowaniem należy wyczyścić bufor szablonu i ustawić operację bufora szablonu tak, aby piksele lustra miały w nim ustawioną niezerową wartość; bufor szablonu – „stencil buffer” – to kolejny bufor obok bufora głębokości i buforów koloru bufor związany z kontekstem renderowania, więc tworząc kontekst renderowania trzeba upewnić się, że zostanie utworzony także bufor szablonu); - wreszcie rysowana jest cała scena jeszcze raz, ale odbita względem płaszczyzny lustra (przed rysowaniem trzeba wyczyścić bufor głębokości, oraz ustawić test bufora szablonu tak, aby rysowanie odbywało się tylko tam, gdzie widać piksele lustra, a więc gdzie w buforze szablonu jest niezerowa wartość; jeśli za lustrem znajdują się obiekty w oryginalnej scenie, to albo trzeba je zignorować w tym przebiegu, albo włączyć obcinanie rysowanej geometrii płaszczyzną lustra). Można dodać ewentualnie czwarty przebieg i narysować półprzezroczystą powierzchnię lustra w określonym kolorze lub określoną teksturą (np. teksturą rys). Przy rysowaniu odbitej sceny należy pamiętać o odbiciu również świateł. Odbicie geometrii (i świateł) uzyskuje się poprzez ustawienie odpowiednio macierzy przekształcenia świata. Po wykonaniu tego dodatkowo zmieni się orientacja trójkątów, zatem trzeba zmodyfikować odpowiednio ustawienia „backface culling”. Szczególnej uwagi wymaga rysowanie bilbordów odbitych w lustrze. Zadania [5 pkt] – podstawowy efekt lustra [+1 pkt] – jeśli za lustrem znajduje się geometria i jest prawidłowo obcinana [+1 pkt] – jeśli lustro jest półprzezroczyste i ma kształt elipsy [+1 pkt] – jeśli w lustrze widać odbite bilbordy, które ustawiają się prawidłowo do kamery 6. Płaszczyzny obcinania Wprowadzenie DirectX oraz OpenGL umożliwiają zdefiniowanie, zwykle do sześciu, płaszczyzn obcinania geometrii sceny („user clipping planes”). Tylko ta część geometrii sceny, która znajduje się po przedniej stronie aktywnych płaszczyzn obcinania jest rysowana. Do definiowania i włączania płaszczyzn obcinania w OpenGL służą polecenia: glEnable(GL_CLIP_PLANE0+i), glClipPlane(...). W DirectX Managed służy do tego struktura device.ClipPlanes. Zadanie Płaszczyznę można zdefiniować jednoznacznie podając dowolny jej punkt i wektor prostopadły do niej (normalną). Niech w scenie zostanie wykorzystana płaszczyzna obcinania, która przechodzi przez środek stołu i której normalną można dowolnie obracać. Należy narysować scenę dwukrotnie, z różnymi ustawieniami kolorów i tekstur, w pierwszym przebiegu obcinając geometrię po jednej stronie płaszczyzny, w drugim przebiegu po przeciwnej stronie płaszczyzny. Wystarczy jeśli połowa sceny będzie rysowana normalnie, przy użyciu kolorowych materiałów i tekstur, a druga połowa bez użycia tekstur przy pomocy szarych materiałów, ale można oczywiście wybrać w dowolny inny sposób dwa różne zestawy tekstur, materiałów i świateł. [5 pkt] – podział sceny płaszczyzną na dwie części renderowane z różnymi ustawieniami kolorów, świateł, tekstur 7. Bilbordy Wprowadzenie Bilbordem nazywamy półprzezroczystą bitmapę, nałożoną na prostokąt, którego orientacja w przestrzeni dopasowuje się automatycznie do widoku z kamery, podczas gdy położenie środka bilbordu w scenie, oraz jego rozmiary w układzie świata, są stałe. Będziemy zajmować się dwoma wariantami bilbordów. Pierwszy to bilbord, który obraca się tylko wokół jednej osi. Jako przykład można sobie wyobrazić prostokąt z teksturą drzewa, którego obrót ograniczony jest do osi pnia drzewa. Drugi to bilbord, który ustawia się zawsze prostopadle do kierunku patrzenia. Takie bilbordy wykorzystywane są często w połączeniu z systemami cząstek, na przykład do wizualizacji dymu. W przypadku wielu bilbordów, trzeba pamiętać o poprawnym rozwiązaniu problemu przezroczystości. Istnieją na to dwa sposoby: test alfa oraz mieszanie alfa. W przypadku testu alfa, w kanale alfa tekstury zapisana jest maska kształtu, który chcemy wyświetlić. Piksele, którym odpowiada wartość alfa nie spełniająca testu nie są ani rysowane na ekranie, ani uwzględniane w buforze głębokości. Brzegi kształtu uzyskane w ten sposób są poszarpane (problem aliasingu), ale nie ma potrzeby rysowania bilbordów w ustalonej kolejności. Dużo bardziej atrakcyjne efekty zapewnia mieszanie alfa (alpha blending), kiedy to możemy w teksturze bilbordu określić stopień przezroczystości każdego jej fragmentu. W takim przypadku konieczne jest sortowanie bilbordów po współrzędnej głębokości w układzie kamery i rysowanie od najdalszych do najbliższych. Zadanie Umieść w scenie półprzezroczysty bilbord [3 pkt] [+1 pkt] – za oba rodzaje bilbordów [+1 pkt] – za możliwość przełączania sposobu renderowania przezroczystości pomiędzy testem alfa oraz mieszaniem alfa [+1 pkt] – za poprawne sortowanie dla co najmniej dwóch bilbordów renderowanych z użyciem mieszania alfa [+2 pkt] – za zrealizowanie systemu cząstek, czyli zbioru bilbordów poruszających się w czasie, z których stare bilbordy (cząstki) giną po spełnieniu pewnych ustalonych warunków, a na ich miejsce tworzone są nowe bilbordy w punkcie (lub na powierzchni obiektu) zwanym generatorem cząstek 8. Mgła Wprowadzenie Karty graficzne wspomagające grafikę 3D pozwalają dokonać interpolacji pomiędzy wyliczonym kolorem piksela, a zadanym kolorem mgły, na podstawie położenia renderowanego fragmentu powierzchni względem kamery. W prostszym przypadku interpolacja odbywa się na podstawie współrzędnej z w układzie kamery i jest wykonywana sprzętowo dla każdego piksela, w bardziej złożonym na podstawie odległości od kamery (range based fog) i wtedy obliczenia mgły wykonywane są dla wierzchołków trójkątów. Mgła często jest używana do tego, by ukryć fakt obcięcia geometrii dalszą płaszczyzną obcinania w rzutowaniu perspektywicznym. Zadanie Dodać dowolny rodzaj mgły do sceny, z możliwością jej włączania/wyłączania i zmiany gęstości. [4 pkt] 9. Renderowanie do tekstury Renderowanie do tekstury polega na tym, że tworzony obraz nie zostaje zapisany w buforze koloru, tylko w teksturze. W kolejnym przebiegu renderowania, gdy renderowany jest już obraz do buforu koloru, tekstura ta może zostać wykorzystana do nałożenia na obiekty w scenie. Przykładowo możemy wyobrazić sobie monitor umieszczony w scenie, na który nakładana jest tekstura widoku tej właśnie sceny. Ciekawe efekty można uzyskać łącząc renderowanie do tekstury z renderowaniem prostokątów pełnoekranowych. Renderowanie prostokąta pełnoekranowego polega na: ustawieniu jednostkowej macierzy dla projekcji i przekształceń widoku/świata, narysowaniu pojedynczego prostokąta równoległego do ekranu, który wypełni całe okno widoku. Jeśli ustawimy kanał alfa dla koloru materiału prostokąta pełnoekranowego, oraz nałożymy na jego powierzchnię teksturę z wyrenderowanym właściwym obrazem sceny, możemy: a) uzyskać efekt rozmycia ruchu (gdy nie czyścimy nigdy bufora koloru, a rysując prostokąt pełnoekranowy z nałożonym obrazem sceny, używamy mieszania typowego dla przezroczystości: src_alpha * src_color + (1-src_alpha) * dst_color; wielkość efektu zależy od wartości kanału alfa koloru materiału prostokąta) b) uzyskać efekt poświaty (gdy renderujemy kilka wersji prostokąta pełnoekranowego z nałożoną teksturą z widokiem sceny i z przypisanym stałym kanałem alfa, z których każda kolejna jest nieco większa i używamy mieszania addytywnego: src_alpha * src_color + dst_color) W DirectX Managed instrukcje przydatne do realizacji renderowania do tekstury to: new Texture(device, w, h, 1, Usage.RenderTarget, Format.X8R8G8B8, Pool.Default); device.SetRenderTarget(...); (dodatkowo może się przydać obiekt klasy RenderToSurface). W OpenGL na nowszych kartach, wspierających odpowiednie rozszerzenie, warto skorzystać z instrukcji podobnych jak poniżej: glGenFramebuffersEXT(1, &fbo); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, colorTex, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depthTex, 0); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,...);, na prawie wszystkich kartach graficznych można skorzystać z jednej prostej instrukcji: glCopyTexImage2D(...);W tym drugim przypadku odbywa się faktycznie kopiowanie z bufora koloru do tekstury, więc tekstura musi mieć identyczne rozmiary i format jak bufor koloru (trzeba wziąć pod uwagę, że starsze karty graficzne mogą nie obsługiwać tekstur, których wymiary nie są potęgami dwójki). Czasem w OpenGL można poradzić sobie bez renderowania do tekstury z pomocą bufora akumulacji, którego z kolei nie ma DirectX. Zadania Wykorzystaj wyrenderowany obraz jako teksturę w scenie. [2 pkt] Zrealizuj opisane powyżej: rozmycie ruchu lub efekt poświaty. [4 pkt] 10. Proceduralne generowanie tekstur Wprowadzenie Szum Perlina dobrze modeluje wiele zjawisk ze świata przyrody, dlatego jest powszechnie wykorzystywany w grafice komputerowej między innymi do: zaburzeń powierzchni wody stojącej, generowania rzeźby górzystego terenu, tworzenia tekstur chmur, dymu, mgły, drewna i marmuru. Matematycznie jest to suma kilku tzw. oktaw szumu (funkcji losowej o rozkładzie równomiernym, wartości średniej zero i zadanej maksymalnej amplitudzie), z których każda kolejna oktawa powstaje przez dwukrotne zwiększenie amplitudy i dwukrotne zmniejszenie częstotliwości (co odpowiada dwukrotnemu rozciągnięciu w każdym wymiarze). Więcej informacji pod adresem http://freespace.virgin.net/hugo.elias/models/m_perlin.htm . Teksturę szumu Perlina można znaleźć wpisując w wyszukiwarce obrazów „Perlin noise”. Zadanie Utworzyć i użyć w scenie tekstury drewna lub marmuru wygenerowanej na podstawie szumu Perlina. Wyobraźmy sobie falę rozchodzącą się z określonego punktu płaszczyzny: z(x,y) = A * cos( B*r(x,y) + C) + D , gdzie A, B, C, D to pewne stałe, a r(x,y) to odległość punktu płaszczyzny od źródła fali. Jeśli pobierzemy wartość dwuwymiarowego szumu Perlina dla współrzędnych (x,y), zaburzymy nią wartość promienia r(x,y), a otrzymaną wartość z(x,y) wykorzystamy do zmieszania w odpowiedniej proporcji dwóch kolorów, to otrzymamy obraz, który może przypominać słoje drewna. Można poeksperymentować również z innymi rodzajami fal niż fala kulista np. ze złożeniem kilku fal płaskich: cos(a1*x+b1*y+c1)*f1+...+cos(an*x+bn*y+cn)*fn , gdzie a1,...,an, b1,...,bn, c1,...,cn, f1,...,fn to pewne stałe. Wartość szumu Perlina najprościej pobrać z piksela wczytanej w programie odpowiedniej bitmapy znalezionej w internecie. [6 pkt]