1. Prymitywy graficzne Prymitywy graficzne są elementarnymi obiektami jakie potrafi bezpośrednio rysować, określony system graficzny (DirectX, OpenGL itp.) są to: punkty, listy linii, serie linii, listy trójkątów i serie trójkątów. 1.1 Lista punktów (point list) System graficzny wyświetlający listę punktów rysuje na ekranie punkty w takiej kolejności w jakiej zapisano je w odpowiedniej tablicy, czy też buforze wierzchołków. Typowym zastosowaniem tego typu prymitywów graficznych jest rysowanie gwiazd. P2(-5,10,1) P4(5,10,1) P1(-10,1,1) P3(0,1,1) P5(10,1,1) Rys 1 przedstawiający w jaki sposób rysowana jest lista punków. TVector3 Points[] = TVector3(-10.0, 1.0, 1.0), TVector3( -5.0, 10.0, 1.0), TVector3( 0.0, 1.0, 1.0), TVector3( 5.0, 10.0, 1.0), TVector3( 10.0, 1.0, 1.0), 1.2 Lista linii (line list) System graficzny wyświetlając listę linii pobiera z bufora (tablicy) wierzchołków parę sąsiadujących elementów i rysuje na ekranie linie łączącą te dwa wierzchołki. Także oczywiste jest, że dla narysowania N linii bufor powinien zawierać 2N wierzchołków. Dodatkowo należy pamiętać o odpowiedniej organizacji (ułożeniu) wierzchołków, aby narysować obiekt o żądanych kształtach. P2(10,15,0) P4(20,15,0) P1(5,5,0) P3(15,5,0) Rys. 2 przedstawiający w jaki sposób łączone są wierzchołki rysowanymi liniami Linie mogą być wykorzystywane do reprezentacji krawędzi brył, tzw. druciaków, przy rysowaniu efektu deszczu lub do imitowania wszelkiego typu strzałów lasera. W niektórych - 1 -
systemach graficznych (np. DirectX) znacznie poważniejszym zastosowaniem linii jest antialiasing krawędziowy, który polega na dwukrotnym rysowaniu sceny z tym, że drugim razem używamy tylko linii, które są w odpowiedni sposób wygładzane. TVector3 Vertices[] = TVector3(-5.0, 5.0, 0.0), TVector3(-10.0, 15.0, 0.0), TVector3(-15.0, 5.0, 0.0), TVector3(-20.0, 15.0, 0.0), 1.3 Seria linii (line strip) W tym trybie linie rysowane są na zasadzie łączenia kolejnych wierzchołków, także wierzchołki muszą być odpowiednio ułożone w buforze, aby w efekcie dać sensowny rysunek. Ten typ prymitywów znakomicie nadaje się do rysowania np. płaszczyzn generowanych w programowo. P2(10,15,0) P4(20,15,0) P1(5,5,0) P3(15,5,0) Rys. 3 przedstawiający w jaki sposób łączone są kolejne wierzchołki rysowanymi liniami, dając w efekcie łamaną Należy pamiętać, iż rysowanych linii będzie o jedną mniej niż wierzchołków. TVector3 Vertices[] = TVector3(-5.0, 5.0, 0.0), TVector3(-10.0, 15.0, 0.0), TVector3(-15.0, 5.0, 0.0), TVector3(-20.0, 15.0, 0.0), - 2 -
1.4 Lista trójkątów (triangle list) Trójkąty są podstawą wszelkiego typu wypełnianych brył. Prymitywy będące listą trójkątów tworzone są z kolejnych trzech wierzchołków, których liczba musi być podzielna przez trzy. P2(0,15,0) P4(10,15,0) P6(20,15,0) P1(-5,5,0) P3(5,5,0) P5(15,5,0) Rys. 4 przedstawiający sposób rysowania trójkątów, w oparciu o listę trójkątów Podczas operacji rysowania wnętrze trójkąta może być wypełnione pojedynczym kolorem, teksturą lub kolory będą interpolowane między wierzchołkami TVector3 Vertices[] = TVector3(-5.0, -5.0, 0.0), TVector3( 0.0, 5.0, 0.0), TVector3( 5.0, -5.0, 0.0), TVector3(10.0, 5.0, 0.0), TVector3(15.0, -5.0, 0.0), TVector3(20.0, 5.0, 0.0) 1.5 Seria trójkątów (Triangle Strip) Przy użyciu tego typu prymitywów rysowana jest seria (taśma ang. strip) trójkątów. Kolejne trójkąty wyznaczane są przez trzy wierzchołki począwszy od wierzchołka będącego numerem trójkąta w taśmie. I tak dla pierwszego trójkąta pobierane są wierzchołki 1,2,3, dla drugiego 2,3,4 itd. Także od razu widać, iż można narysować N-2 trójkątów, gdzie N- liczba wierzchołków. Tak jak Line Strip, ten typ prymitywów znakomicie służy do rysowania powierzchni generowanych programowo oraz do rysowania dowolnych wielokątów na podstawie podanych wierzchołków. P2(0,15,0) P4(10,15,0) P1(-5,5,0) P3(5,5,0) P5(15,5,0) Rys. 5 przedstawia sposób łączenia kolejnych wierzchołków w trójkąty. - 3 -
TVector3 Vertices[] = TVector3(-5.0, -5.0, 0.0), TVector3( 0.0, 5.0, 0.0), TVector3( 5.0, -5.0, 0.0), TVector3(10.0, 5.0, 0.0), TVector3(15.0, -5.0, 0.0), TVector3(20.0, 5.0, 0.0) 1.6 Triangle Fan W tym typie prymitywów wszystkie rysowane trójkąty mają wspólny wierzchołek i jest nim pierwszy punkt w buforze. Przy rysowaniu pierwszego trójkąta pobierane są wierzchołki 1,2,3, przy rysowaniu drugiego 1,3,4 itd. P2(0,15,0) P3(1,10,0) P1(-5,5,0) P4(3,5,0) P5(2,0,0) P6(1,-5,0) Rys. 6 przedstawia sposób współdzielenia pierwszego wierzchołka przez wszystkie trójkąty TVector3Vertices[] = TVector3(-5.0, 5.0, 0.0), TVector3( 0.0,15.0, 0.0), TVector3( 1.0,10.0, 0.0), TVector3( 3.0, 5.0, 0.0), TVector3( 2.0, 0.0, 0.0), TVector3( 1.0,-5.0, 0.0), 1.7 Indeksowane listy linii i trójkątów. Indeksowane listy linii i trójkątów są bardzo podobne do zwykłych list linii i trójkątów, jedyną różnicą jest to, że system graficzny (lub nasz program) pobiera pary lub trójki wierzchołków z tablicy na podstawie informacji zawartej w tablicy (buforze) indeksów. Przykład organizacji brył z zastosowaniem tego typu prymitywów znajduje się w podrozdziale 2.2 i 2.3. - 4 -
2. Reprezentacja obiektów 2.1 Reprezentacja punktowa Tego typu metoda oczywiście jest najprostsza i wymaga mniejszej ilości danych niż pozostałe (krawędziowa, wypełniana). W reprezentacji punktowej potrzebne są jedynie współrzędne (x,y,z) kolejnych punktów tworzących daną bryłę Przykładowa konstrukcja sześcianu może wyglądać następująco: TVector3v[8]; v[0] = TVector3 (-4.5, 4.5,-4.5 ); v[1] = TVector3 ( 4.5, 4.5,-4.5 ); v[2] = TVector3 ( 4.5,-4.5,-4.5 ); v[3] = TVector3 (-4.5,-4.5,-4.5 ); v[4] = TVector3 (-4.5, 4.5, 4.5 ); v[5] = TVector3 ( 4.5, 4.5, 4.5 ); v[6] = TVector3 ( 4.5,-4.5, 4.5 ); v[7] = TVector3 (-4.5,-4.5, 4.5 ); Tak zdefiniowana kostka zbudowana jest z 8 punktów leżących symetrycznie względem punktu (0,0,0) układu współrzędnych oraz w odległości równej 9 jednostek od siebie. W zasadzie bryła zdefiniowana w ten sposób może zostać bezpośrednio narysowana na ekranie. Przypomnę, że każdy z wierzchołków sześcianu powinien zostać poddany właściwym transformacjom, czyli przemnożony kolejno przez: macierz transformacji świata, macierz widoku, macierz projekcji i macierz skalowania pola widoku. Dodatkowo należy pamiętać o podzieleniu przez współrzędną w każdego z otrzymanych, w wyniku transformacji, wektorów. W przypadku reprezentacji punktowej nie musimy zajmować się problemem usuwania niewidocznych powierzchni, czy punktów. Rys. 7 Przykład reprezentacji punktowej obiektu - 5 -
2.2 Reprezentacja krawędziowa Wizualizacja szkieletowa polega na rysowaniu krawędzi sianek danego obiektu. W metodzie tej poza współrzędnymi wierzchołków potrzebujemy informacji, które z wierzchołków mamy łączyć liniami. Tego typu tablica często nazywana jest indeksem wierzchołków tworzących linie. Przyglądając się rysunkowi sześcianu, mieszczącemu się na sąsiedniej stronie, spróbujemy teraz stworzyć tablicę połączeń dla linii. Może ona wyglądać tak: WORD Line_Index[]= 0,1,1,2,2,3,3,0, // linie tworzące ściankę przednią 4,5,5,6,6,7,7,4, // linie tworzące ściankę tylną 0,4,1,5,3,7,2,6, // pozostałe linie Rys 8 Przykład reprezentacji krawędziowej (szkieletowej) obiektu Chcąc narysować bryłę zdefiniowaną w ten sposób należy tak jak poprzednio każdy z wierzchołków sześcianu przemnożyć kolejno przez: macierz transformacji świata, macierz widoku, macierz projekcji i macierz skalowania pola widoku oraz podzielić przez współrzędną w. Następnie mając wyznaczone współrzędne punktów w przestrzenie ekranu można przejść do właściwej procedury rysującej linie między odpowiednimi wierzchołkami. W pseudokodzie może wyglądać to następująco:... TVector3 tab2d[8];/*tablica współrzędnych punktów w przestrzeni ekranu*/ - 6 -
... for(int a=0;a<ilość_krawędzi;a++) int x1,y1,x2,y2; x1=tab2d[line_index[a*2]].x; y1=tab2d[line_index[a*2]].y; x2=tab2d[line_index[a*2+1]].x; y2=tab2d[line_index[a*2+1]].y; Line(x1,y1,x2,y2);} V4 V5 V0 V1 V7 V6 V3 V2 Rys 9.Sześcian z zaznaczonymi krawędziami oraz trójkątami, tworzącymi jego ścianki W przypadku definiowania połączeń między wierzchołkami tworzącymi linie nie ma specjalnych wymogów dotyczących kolejności wierzchołków. Jedynym kryterium jest kształt uzyskany w wyniku połączeń. Następstwem tych faktów jest niemożliwość eliminacji linii, które należałoby uznać za niewidoczne, także obiekty rysowane za pomocą linii w pełni oddają druciane modele brył. 2.3 Reprezentacja wypełniana Reprezentacja wypełniana polega na rysowaniu pojedynczych ścianek bryły z zastosowaniem określonego modelu cieniowania (płaskie, Gouraud a, Phong a). Metoda ta bazuje na siatkach wielokątów - które są zbiorem punktów, krawędzi oraz wielokątów tak połączonych, że każda z krawędzi jest wspólna przynajmniej dla dwóch wielokątów. Krawędź zawsze łączy dwa wierzchołki, a wielokąt jest zamkniętą sekwencją krawędzi. Dla naszych potrzeb stosowane wielokąty ograniczymy jedynie do trójkątów, gdyż dowolny wielokąt może być reprezentowany odpowiednią liczbą trójkątów. Jak widać na rys. 9 sześcian może być reprezentowany za pomocą 6 czworokątnych ścian lub 12 trójkątnych. Chcąc zdefiniować pełny opis sześcianu poza tablicą współrzędnych wierzchołków musimy stworzyć odpowiednią tablicę definiującą numery wierzchołków przynależnych do danej ścianki, dodatkowo musimy zadbać, aby ich kolejność była zgodna z ruchem wskazówek zegara. - 7 -
Przykładowo, przednia ścianka sześcianu z rys. 3 tworzona jest z dwóch trójkątów, które powinny składać się z wierzchołków wymienionych w następującej kolejności trójkąt 1: v0, v1, v2; trójkąt 2: v2,v3, v0. Podążając tym tokiem rozumowania możemy stworzyć tablicę indeksów wierzchołków, dla wszystkich 12 trójkątów tworzących sześcian. Ma ona następującą postać WORD Triangle_Index[]= 0,1,2,2,3,0, // przednia ścianka 6,5,4,4,7,6, // tylna ścianka 4,5,1,1,0,4, // górna ścianka 1,5,6,6,2,1, // prawa ścianka 7,4,0,0,3,7, // lewa ścianka 2,6,7,7,3,2 // dolna ścianka Rys 10 Przykład reprezentacji wypełnianej obiektu z zastosowaniem cieniowania Gouraud a. Procedura rysowania bryły wypełnianej jest podobna do poprzedniej także: każdy z wierzchołków sześcianu przemnożyć kolejno przez: macierz transformacji świata, macierz widoku, macierz projekcji i macierz skalowania pola widoku oraz podzielić przez współrzędną w. Następnie mając wyznaczone współrzędne punktów w przestrzenie ekranu można przejść do właściwej procedury rysującej wypełniane trójkąty, składające się z odpowiednich wierzchołków. W pseudokodzie może wyglądać to następująco: - 8 -
... TVector3 tab2d[8];/*tablica współrzędnych punktów w przestrzeni ekranu*/... for(int a=0;a<ilość_scian;a++) int x1,y1,x2,y2,x3,y3; x1=tab2d[triangle _Index[a*3]].x; y1=tab2d[triangle _Index[a*3]].y; x2=tab2d[triangle _Index[a*3+1]].x; y2=tab2d[triangle _Index[a*3+1]].y; x3=tab2d[triangle _Index[a*3+2]].x; y3=tab2d[triangle _Index[a*3+2]].y; Triangle(x1,y1,x2,y2,x3,y3); } Zadania do wykonania w trakcie ćwiczeń Napisać procedurę reprezentującą bryłę w postaci punktowej Napisać procedurę rysującą linie od punktu (x1,y1) do punktu (x2,y2) Napisać procedurę reprezentującą bryłę w postaci szkieletowej Napisać procedurę reprezentującą bryłę w postaci wypełnianej (zastosować cieniowanie płaskie lub Gouraud a). Procedura powinna sprawdzać widoczność ścian. Rozszerzyć program z poprzedniego punktu o algorytm z-bufora. Algorytm z-bufor należy zaimplementować w funkcji cieniującej wielokąty. - 9 -