Grafika komputerowa. OpenGL. Podstawy

Podobne dokumenty
6 Przygotował: mgr inż. Maciej Lasota

Światło. W OpenGL można rozróżnić 3 rodzaje światła

Światła i rodzaje świateł. Dorota Smorawa

Temat: Transformacje 3D

OpenGL Światło (cieniowanie)

Oświetlenie w OpenGL. Oprogramowanie i wykorzystanie stacji roboczych. Wykład 8. Światło otaczajace. Światło rozproszone.

OpenGL Światło (cieniowanie)

Materiały. Dorota Smorawa

Laboratorium grafiki komputerowej i animacji. Ćwiczenie V - Biblioteka OpenGL - oświetlenie sceny

OpenGL model oświetlenia

2 Przygotował: mgr inż. Maciej Lasota

3 Przygotował: mgr inż. Maciej Lasota

Mieszanie kolorów. Dorota Smorawa

Rzutowanie DOROTA SMORAWA

Zatem standardowe rysowanie prymitywów wygląda następująco:

Janusz Ganczarski. OpenGL Pierwszy program

Zadanie 1. Ściana. 1. Potrzebne zmienne w dołączonym do zadania kodzie źródłowym

GRAFIKA KOMPUTEROWA 7: Kolory i cieniowanie

Laboratorium 1. Część I. Podstawy biblioteki graficznej OpenGL.

1 Wstęp teoretyczny. Temat: Manipulowanie przestrzenią. Grafika komputerowa 3D. Instrukcja laboratoryjna Układ współrzędnych

Studium podyplomowe. Programowanie w OpenGL. Michał Turek, AGH Kraków

Elementarne obiekty geometryczne, bufory. Dorota Smorawa

Wprowadzenie do programowania z wykorzystaniem biblioteki OpenGL. Dorota Smorawa

Tekstury. Dorota Smorawa

OpenGL teksturowanie

Grafika Komputerowa Wykład 5. Potok Renderowania Oświetlenie. mgr inż. Michał Chwesiuk 1/38

Janusz Ganczarski. OpenGL Definiowanie sceny 3D

8 Przygotował: mgr inż. Maciej Lasota

1. Prymitywy graficzne

Prosty program- cpp. #include <GL/glut.h>

OpenGL oświetlenie. Bogdan Kreczmer. Katedra Cybernetyki i Robotyki Wydziału Elektroniki Politechnika Wrocławska

Przekształcenia geometryczne. Dorota Smorawa

Animowana grafika 3D. Opracowanie: J. Kęsik.

System graficzny. Potok graficzny 3D. Scena 3D Zbiór trójwymiarowych danych wejściowych wykorzystywanych do wygenerowania obrazu wyjściowego 2D.

Oprogramowanie i wykorzystanie stacji roboczych. Wykład 6

Oświetlenie. Modelowanie oświetlenia sceny 3D. Algorytmy cieniowania.

Grafika 3D program POV-Ray - 1 -

GRAFIKA CZASU RZECZYWISTEGO Wprowadzenie do OpenGL

Allegro5 3/x. Przykład wklejamy go do dev'a zamiast kodu domyślnego dal programu z allegro i kompilujemy.

Wprowadzenie do rysowania w 3D. Praca w środowisku 3D

Rysowanie punktów na powierzchni graficznej

Systemy wirtualnej rzeczywistości. Komponenty i serwisy

Temat: Wprowadzenie do OpenGL i GLUT

Co to jest OpenGL? Oprogramowanie i wykorzystanie stacji roboczych. Wykład 5. OpenGL - Achitektura. OpenGL - zalety. olas@icis.pcz.

DesignCAD 3D Max 24.0 PL

Teksturowanie (ang. texture mapping)

Teksturowanie. Oprogramowanie i wykorzystanie stacji roboczych. Wykład 10. Tekstury. Proces nakładania tekstury.

Ćwiczenie 4 - Podstawy materiałów i tekstur. Renderowanie obrazu i animacji

OpenGL przezroczystość

Aleksandra Zając. Raport. Blender. Pokemon: Eevee

Grafika Komputerowa Materiały Laboratoryjne

GRAFIKA KOMPUTEROWA 8: Konwersja i mieszanie kolorów

Transformacje. dr Radosław Matusik. radmat

Oświetlenie obiektów 3D

Grafika 3D OpenGL część II

Efekt lustra 3D w OpenGL z wykorzystaniem bufora szablonowego (stencil buffer)

Bufor koloru cd. Czyszczenie bufora koloru glclearcolor( )

Mapowanie tekstur Mip-mapy (level of detail) Filtrowanie Multiteksturowanie

Oprogramowanie i wykorzystanie stacji roboczych. Wykład 5

Plan wykładu. Akcelerator 3D Potok graficzny

Ćwiczenie 6 Animacja trójwymiarowa

Misja#3. Robimy film animowany.

Podstawy programowania, Poniedziałek , 8-10 Projekt, część 1

Grażyna Koba. Grafika komputerowa. materiały dodatkowe do podręcznika. Informatyka dla gimnazjum

Laboratorium 4 OpenGl (4) Oświetlenie sceny

0. OpenGL ma układ współrzędnych taki, że oś y jest skierowana (względem monitora) a) w dół b) w górę c) w lewo d) w prawo e) w kierunku do

Ustawienia materiałów i tekstur w programie KD Max. MTPARTNER S.C.

Grafika komputerowa INSTRUKCJA DO LABORATORIUM 2: operacje przestrzenne oraz obsługa klawiatury i myszki

Ćwiczenie 1 Automatyczna animacja ruchu

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Baltie 3. Podręcznik do nauki programowania dla klas I III gimnazjum. Tadeusz Sołtys, Bohumír Soukup

Grafika Komputerowa Wykład 6. Teksturowanie. mgr inż. Michał Chwesiuk 1/23

glwindowpos2d void DrawString (GLint x, GLint y, char *string) { glwindowpos2i (x,y); int len = strlen (string); for (int i = 0; i < len; i++)

Rysunek 1: Okno timeline wykorzystywane do tworzenia animacji.

Grafika komputerowa Tekstury

Tworzenie prezentacji w MS PowerPoint

Wykład 12. Wprowadzenie do malarstwa, str. 1 OpenGL Open Graphics Library. OpenGL składa się z

Gry Komputerowe Laboratorium 4. Teksturowanie Kolizje obiektów z otoczeniem. mgr inż. Michał Chwesiuk 1/29. Szczecin, r

OpenGL Zaawansowana grafika komputerowa

OpenGL : Oświetlenie. mgr inż. Michał Chwesiuk mgr inż. Tomasz Sergej inż. Patryk Piotrowski. Szczecin, r 1/23

Wykład 12. Wprowadzenie do malarstwa, str. 1 OpenGL Open Graphics Library. OpenGL składa się z

Przy dużej wielkości głębokości uzyskamy wrażenie nieskończoności: Dla głębokości zerowej uzyskamy tekst płaski:

Grafika Komputerowa Wykład 4. Synteza grafiki 3D. mgr inż. Michał Chwesiuk 1/30

// Potrzebne do memset oraz memcpy, czyli kopiowania bloków

Animowana grafika 3D. Opracowanie: J. Kęsik.

OpenGL. Silicon Graphics (IRIS GL stacje graficzne)

Rysowanie precyzyjne. Polecenie:

KGGiBM GRAFIKA INŻYNIERSKA Rok III, sem. VI, sem IV SN WILiŚ Rok akademicki 2011/2012

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

OPENGL PRZEKSZTAŁCENIA GEOMETRYCZNE

GLKit. Wykład 10. Programowanie aplikacji mobilnych na urządzenia Apple (IOS i ObjectiveC) #import "Fraction.h" #import <stdio.h>

Program 6. Program wykorzystujący strukturę osoba o polach: imię, nazwisko, wiek. W programie wykorzystane są dwie funkcje:

Tematy lekcji zajęć komputerowych klasa 5b grupa 1 i grupa 2

Techniki wstawiania tabel

Laboratorium Grafiki Komputerowej i Animacji. Ćwiczenie V. Biblioteka OpenGL - oświetlenie sceny

Lab 9 Podstawy Programowania

Programowanie strukturalne i obiektowe. Funkcje

OpenGL oświetlenie i tekstury. OpenGL oświetlenie. Bogdan Kreczmer.

Lekcja 5 - PROGRAMOWANIE NOWICJUSZ

Ćwiczenie 1 Galeria zdjęć

Transkrypt:

Grafika komputerowa OpenGL Podstawy

Czym jest OpenGL? "Programowy interfejs sprzętu graficznego" Biblioteka zawierająca zbiór procedur ułatwiających rysowanie grafiki dwu i trójwymiarowej. OpenGL nie jest, ani nie zawiera "enginu" grafiki trójwymiarowej służy jedynie do rysowania. Procedury służące do rysowania skomplikowanych modeli trójwymiarowych, ich animacji i transformacji powinny być zaimplementowane przez programistę.

Czym jest OpenGL? OpenGL nie zawiera funkcji służących do tworzenia okien i ich zarządzania (od tego jest np. GLUT), ani do interakcji z użytkownikiem. Procedury OpenGL służą do rysowania prymitywów (punktów, odcinków, trójkątów), które mogą być cieniowane, pokryte teksturami, przeźroczyste itd. Implementacje OpenGL w różnych środowiskach mogą, ale nie muszą wykorzystywać akceleracji sprzętowej. Wykorzystanie dostępnych mechanizmów zależy od implementacji OpenGL i jest przeźroczyste dla programisty.

Czym jest OpenGL? Biblioteka OpenGL pracuje na zasadzie maszyny stanów. W praktyce polega to na tym, że w danym momencie pracy jest ustawiony zbiór opcji wg. których rysowane są prymitywy (np. kolor, sposób cieniowania itp.). Sprawia to pewne problemy przy konstrukcji enginu do np. gry komputerowej, gdyż zawsze, kiedy zaczynamy rysować jakiś obiekt należy pamiętać, żeby ustawić ponownie wszystkie parametry rysowania, gdyż niektóre mogły zostać zmienione podczas rysowania innych obiektów.

Nazwy procedur w OpenGL (1) W OpenGL praktycznie każda procedura posiada kilka wersji. Wersje te różnią się przede wszystkim typem parametrów. Jeżeli procedura może mieć wersje różniące się liczbą parametrów, to do nazw konkretnych wersji dodawany jest przyrostek (cyfra) określający liczbę tych parametrów.

Nazwy procedur w OpenGL (2) W zależności od typu parametrów dodawane są przyrostki, np: d double f float i int Jeżeli istnieją wersje procedury, z których jedna przyjmuje jako parametr liczbę, a druga wektor, to dodawany jest również przyrostek v.

Nazwy procedur w OpenGL (3) Przykłady: glvertex3d posiada trzy parametry typu double gllightfv jako jeden z parametrów przyjmuje wektor liczb typu float

Czyszczenie okna (1) Wyczyszczenie zawartości okna polega na zapisaniu każdego piksela takim samym kolorem. Kolor czyszczenia można ustawić za pomocą procedury glclearcolor. Składnia: void glclearcolor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha ); red, green, blue to składowe koloru, alpha to stopień przezroczystości.

Czyszczenie okna (2) Do czyszczenia okna służy procedura glclear. Składnia: void glclear( GLbitfield mask ) Parametr mask jest mapą bitową tworzoną przez superpozycję pewnych stałych. Określa które bufory powinny być czyszczone. Np.: GL_COLOR_BUFFER_BIT obraz GL_DEPTH_BUFFER_BIT - z-bufor

Wyświetlanie wyników rysowania Jeżeli pracujemy w oknie z podwójnym buforowaniem konieczna jest dodatkowa operacja aby wyświetlić wynik rysowania. W podwójnym buforowaniu rysujemy na niewidocznym buforze. Aby go pokazać należy wywołać procedurę: glutswapbuffers; Składnia: void glutswapbuffers(void);

Ćwiczenie Do części inicjującej szkieletu dodaj procedurę określającą kolor czyszczenia na niebieski. Do procedury odświeżającej okno dodaj wywołanie procedury czyszczącej okno (czyść zarówno bufor kolorów jak i z-bufor (to drugie przyda się w następnych ćwiczeniach). Nie zapomnij na końcu procedury odświeżającej dodać wywołania procedury glutswapbuffers. W wyniku ćwiczenia powinieneś otrzymać okno, które jest wypełnione kolorem niebieskim, zamiast przypadkowych obrazów.

Układy współrzędnych (1) Przestrzeń Przekształcenie modelu obiektu Przestrzeń świata Przekształcenie widoku Przestrzeń Przekształcenie rzutowania Przestrzeń oka przycięcia Podział perspektywiczny Znormalizowana przestrzeń urządzenia Przekształcenie obrazu i zakresu głębi Przestrzeń okna

Układy współrzędnych (2) Współrzędne homogeniczne punktu w trójwymiarowej przestrzeni są reprezentowane przez wektor 4 liczb: <x,y,z,w>. Normalne współrzędne trójwymiarowe można uzyskać dzieląc współrzędne x, y i z przez w. Kolejne przekształcenia są reprezentowane przez macierze 4x4. Dzięki dodatkowej współrzędnej transformacje wymagające dodania stałej do współrzędnej x, y albo z (np. przesunięcie) mogą być również przedstawione w postaci macierzy.

Układy współrzędnych (3) Przestrzeń obiektu układ współrzędnych charakterystyczny dla pojedynczego rysowanego obiektu. Przestrzeń świata układ współrzędnych rysowanej sceny. Przekształcenie modelu (model) przekształcenie przestrzeni obiektu w przestrzeń świata. Przestrzeń oka układ współrzędnych w którym obserwator znajduje się w początku układu współrzędnych, patrzy w kierunku rosnących wartości współrzędnych z (w OpenGL malejących), a góra to dodatni kierunek współrzędnej y.

Układy współrzędnych (4) Przekształcenie widoku (view) przekształcenie przestrzeni świata w przestrzeń oka. Przestrzeń przycięcia przestrzeń definiuje widziany obszar. Wszystko co jest widoczne znajduje się wewnątrz sześcianu o krawędziach równoległych do osi układu współrzędnych. Każdy widoczny punkt musi spełniać warunki: -w x w, -w y w, -w z w. Przekształcenie rzutowania (projection) przekształcenie przestrzeni oka w przestrzeń przycięcia.

Układy współrzędnych (5) Znormalizowana przestrzeń urządzenia w przestrzeni urządzenia wszystkie współrzędne homogeniczne są przekształcone do ich reprezentacji trójwymiarowej (w=1). Podział perspektywiczny transformacja przestrzeni przycięcia do znormalizowanej przestrzeni urządzenia. Przestrzeń okna przestrzeń w której współrzędne są wyrażone w pikselach w oknie, w którym obraz jest ostatecznie rysowany.

Układy współrzędnych (6) W OpenGL macierze przekształceń modelu i widoku są połączone w macierz model widok będącą złożeniem obu tych transformacji. Postąpiono w ten sposób, gdyż w zasadzie jedna z tych macierzy może zastąpić drugą. Możemy modyfikować w dowolny sposób macierz model widok i macierz rzutowania. Przekształcenie obrazu można modyfikować za pomocą procedury glviewport, oraz za pomocą procedur biblioteki GLUT. Tym przekształceniem nie będziemy się zajmować na zajęciach. Procedurę glviewport wykorzystuje się do zainicjowania macierzy przekształcenia obrazu (robione automatycznie przez GLUT) oraz w sytuacjach, gdy okno zmienia rozmiar.

Układy współrzędnych (7) Aby przełączyć OpenGL w tryb modyfikacji odpowiedniej macierzy należy wykorzystać procedurę void glmatrixmode( GLenum mode ), gdzie mode wyznacza którą z macierzy należy modyfikować: GL_PROJECTION macierz rzutowania GL_MODELVIEW macierz model widok.

Układy współrzędnych (8) Obie macierze modyfikuje się poprzez: Załadowanie macierzy jednostkowej za pomocą procedury void glloadidentity(). Mnożenie aktualnej macierzy przez macierze reprezentujące różne transformacje. W przypadku macierzy rzutowania, załadowaną macierz jednostkową mnożymy razy macierz rzutowania równoległego (procedura glortho) lub macierz rzutu perspektywicznego (procedura gluperspective)

Układy współrzędnych (9) W ćwiczeniach będziemy korzystać jedynie z rzutu perspektywicznego. Procedura gluperspective ma następującą składnię: void gluperspective( GLdouble fovy, GLdouble aspect, GLdouble znear, GLdouble zfar ), gdzie: fovy to kąt widzenia (stopnie) aspect to stosunek szerokości okna do jego wysokości znear to odległość bliższej płaszczyzny obcinania zfar to odległość dalszej płaszczyzny obcinania

Układy współrzędnych (10) Poniższy przykładowy kod należy umieścić w części inicjującej szkieletu.... glmatrixmode(gl_projection); //Przełączenie w tryb macierzy rzutowania glloadidentity(); //Załadowanie macierzy jednostkowej gluperspective(55,1,1,50); //Pomnożenie jej razy macierz rzutu perspektywicznego... Przykład ten konfiguruje OpenGL do wykonywania rzutowania perspektywicznego, z kątem widzenia 55 stopni, stosunkiem wysokości do szerokości 1, bliskiej płaszczyźnie obcinania w odległości 1 i dalekiej płaszczyzny obcinania w odległości 50.

Układy współrzędnych (11) Macierz widok model zawiera złożenie transformacji translacji, obrotów i skalowania, które przekształca współrzędne w przestrzeni modelu we współrzędne w przestrzeni oka. Procedury służące do obrotu układu współrzędnych, translacji i skalowania zostaną podane później. Do następnego ćwiczenia przyda się procedura glulookat

Układy współrzędnych (12) Procedura glulookat wykonuje taką transformację układu współrzędnych, aby odpowiadał on widokowi jaki posiada obserwator o określonym położeniu patrzący na konkretny punkt innymi słowy mnoży aktualnie modyfikowaną macierz razy macierz przekształcenia widoku. Składnia: void glulookat( GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz )

Układy współrzędnych (13) Poszczególne parametry tej procedury mają następujące znaczenie: eyex, eyey, eyez współrzędne obserwatora centerx, centery, centerz obserwowany punkt upx, upy, upz wektor określający "górę" obserwatora

Układ współrzędnych (14) Przykładowy kod wykorzystujący opisane wcześniej procedury przedstawiono poniżej. Można go umieścić zarówno w części inicjującej jak i rysującej, ale od tego zależy sposób wykonywania dalszych ćwiczeń. Sugeruję umieszczenie go w części rysującej.... glmatrixmode(gl_modelview); //Przełączenie w tryb macierzy widoku modelu glloadidentity(); //Załadowanie macierzy jednostkowej glulookat(7,7,7,0,0,0,0,1,0); //Patrzy z punktu <7,7,7> na <0,0,0>, głowa prosto ;)...

Prosty obiekt Istnieje wiele procedur rysujących proste obiekty. Oto kilka z nich (nazwy są samotłumaczące ;)) void auxwiretorus(gldouble innerradius, GLdouble outerradius); void auxsolidtorus(gldouble innerradius, GLdouble outerradius); void auxwirecylinder(gldouble radius, GLdouble height); void auxsolidcylinder(gldouble radius, GLdouble height); void auxwireicosahedron(gldouble radius); void auxsolidicosahedron(gldouble radius); void auxwireoctahedron(gldouble radius); void auxsolidoctahedron(gldouble radius); void auxwirebox(gldouble width, GLdouble height, GLdouble depth); void auxsolidbox(gldouble width, GLdouble height, GLdouble depth);

Ćwiczenie Ćwiczenie polega na modyfikacji wyniku poprzedniego ćwiczenia. Zmień kolor tła na czarny. Narysuj szkielet torusa (auxwiretorus) o promieniu wewnętrznym 1 a zewnętrznym 3. Nie zapomnij o ustawieniu obserwatora! Wywołanie auxwiretorus powinieneś umieścić w procedurze rysującej po ustawieniu obserwatora, ale przed glswapbuffers;

Transformacje układu współrzędnych (1) Obrót obiektu można wykonać poprzez pomnożenie macierzy model widok przez macierz obrotu. Procedura void glrotated( GLdouble angle, GLdouble x, GLdouble y, GLdouble z ) mnoży aktualnie modyfikowaną macierz przez macierz obrotu o kąt angle wokół osi wyznaczonej przez wektor <x,y,z>.

Transformacje układu współrzędnych (2) Procedura void glscaled( GLdouble x, GLdouble y, GLdouble z ) mnoży aktualnie modyfikowaną macierz razy macierz skalującą. Kolejne parametry procedury to współczynniki skalowania na poszczególnych osiach układu współrzędnych. Procedura void gltranslated( GLdouble x, GLdouble y, GLdouble z ) mnoży aktualnie modyfikowaną macierz razy macierz translacji. Kolejne parametry procedury to współrzędne wektora, o który następuje przesunięcie.

Ćwiczenie Umieść przed instrukcją rysującą torus (ale za instrukcjami definiującymi obserwatora) intrukcję obracającą układ współrzędnych dookoła osi Y (wektor <0,1,0>) o 60 stopni.

Animacja (1) Animację można zrealizować za pomocą specjalnej procedury callback wywoływanej po określonym czasie. Do ustawienia tej procedury służy procedura: void gluttimerfunc(unsigned int msecs, void (*func)(int value), value); Jako parametr powinno się przekazać nazwę procedury, która jako parametr przyjmuje jedną wartość typu int. Pozostałe parametry to: msecs po ilu milisekundach wołana jest procedura, oraz value jest to wartość która będzie przekazywana procedurze callback jako parametr.

Animacja (2) Procedura callback powinna być wywoływana okresowo i modyfikować wartości jakichś zmiennych na podstawie których rysowany jest obraz w procedurze rysującej i wywołać ponowne narysowanie obrazu. Aby wywołać ponowne narysowanie obrazu należy wywołać procedurę: void glutpostredisplay(void); Aby spowodować ponowne wywołanie procedury callback należy ją ponownie zarejestrować.

Animacja (3) Przykład fragmentów kodu definiujących animację: int angle; void displayframe(void) {... glrotated(angle,1,0,0); auxwireicosahedron(1); glutswapbuffers(); } void nextframe(int value) { angle=(angle+5)%360; gluttimerfunc(41,nextframe,1); glutpostredisplay(); } int main (...) {... gluttimerfunc(41,nextframe,1);... glutmainloop(); }

Ćwiczenie Zmodyfikuj poprzedni program w następujący sposób: dodaj globalną zmienną angle typu int; zmodyfikuj instrukcję obrotu tak, aby obrót następował o stopień angle; Utwórz procedurę void nextframe(int value) w której: zwiększasz zmienną angle o 5 rejestrujesz procedurę callback do ponownego wywołania nakazujesz ponowne narysowanie klatki Dodaj do części inicjującej programu (w funkcji main) rejestrację pierwszego wywołania procedury callback (nextframe)

Kolory Kolor obiektu który ma być narysowany ustawia się za pomocą procedury: void glcolor3d( GLdouble red, GLdouble green, GLdouble blue ) Poszczególne parametry oznaczają udział składowych w wynikowym kolorze i przyjmują wartości z przedziału od 0 do 1.

Ćwiczenie Zmodyfikuj poprzedni program tak, aby animowany torus był niebieski. Spróbuj zastąpić procedurę rysującą siatkę torusa procedurą rysującą pełen torus (auxsolidtorus). Czy efekt jest zadowalający?

Cieniowanie (1) Aby włączyć cieniowanie należy wykonać nastepującą instrukcję: glenable(gl_lighting); glenable służy do włączania wielu różnych opcji, które będą stopniowo wprowadzane. Po włączeniu cieniowania należy włączyć źródło światła: glenable(gl_light0); Domyślnie źródło światła o numerze 0 ma kolor biały i znajduje się w nieskończoności, za obserwatorem.

Cieniowanie (2) Kolejnym krokiem jest włączenie typu cieniowania. Zaimplementowane są dwa algorytmy: płaskie i gładkie (Gourauda). Typ cieniowania wybieramy za pomocą procedury void glshademodel( GLenum mode ), która przyjmuje parametry: GL_FLAT cieniowanie płaskie GL_SMOOTH cieniowanie gładkie

Cieniowanie (3) Przykładowy kod należy umieścić w części inicjującej: glenable(gl_lighting); glenable(gl_light0); glshademodel(gl_smooth); Powyższy kod włącza cieniowanie, zerowe źródło światła i ustawia cieniowanie gładkie

Z-Bufor Z-Bufor służy do usuwania niewidocznych powierzchni. Bufor ten przechowuje informację w jakiej odległości od obserwatora znajduje się aktualnie narysowany piksel. Jeżeli kolejny rysowany w tym miejscu piksel jest dalej, to jest on pomijany, gdyż obecny przesłania go. Aby włączyć Z-Bufor należy wykonać instrukcję: glenable(gl_depth_test); //W części inicjującej

Wstęp do materiałów Każdy obiekt jest zbudowany z "materiału" Materiałem nazywamy zbiór parametrów określających sposób cieniowania obiektu, oraz np. jego teksturę. Aby obiekt złożony z wielokątów (a nie jego szkielet) miał kolor taki jak aktywny należy włączyć śledzenie przez materiał kolorów za pomocą instrukcji: glenable(gl_color_material); //W części inicjującej

Ćwiczenie Zastąp instrukcję rysującą szkielet torusa instrukcją rysującą pełen torus (auxsolidtorus) jeśli tego nie zrobiłeś w poprzednim ćwiczeniu. Włącz cieniowanie, zerowe światło, typ cieniowania płaski. Włącz Z-Bufor i śledzenie kolorów przez materiał. Zmień model cieniowania na gładki. Zaobserwuj różnice.

Interakcja z użytkownikiem (1) Biblioteka GLUT pozwala na zdefiniowanie procedury callback wywoływanej w momencie naciśnięcia klawisza. Procedura ta powinna posiadać następującą sygnaturę: void keyevent(unsigned char c, int x, int y). Oczywiście nazwa jest dowolna. Procedurę callback rejestruje się za pomocą procedury void glutkeyboardfunc( void (*func)(unsigned char key,int x, int y));

Interakcja z użytkownikiem (2) Podobnie jak w przypadku animacji wywołana procedura powinna modyfikować parametry wyświetlania klatki przez procedurę rysującą. Przykład: void keyevent(unsigned char c,int x, int y) { if (c=='a') angle=(angle+5)%360; if (c=='z') angle=(angle-5)%360; glutpostredisplay(); } int main (...) {... glutkeyboardfunc(keyevent);... glutmainloop(); }

Ćwiczenie Zakomentuj zawartość procedury nextframe. Zastąp procedurę rysującą torus inną procedurą, rysującą jakiś obiekt trójwymiarowy. Utwórz procedurę callback pozwalającą na obracanie tym obiektem wokół wszystkich trzech osi układu współrzędnych.

Stos macierzy (1) W trakcie rysowania jednej klatki można wielokrotnie modyfikować macierz widoku modelu w celu rysowania różnych obiektów. Można sobie ułatwić te transformacje zapamiętując aktualny stan macierzy. Do zapamiętania aktualnej macierzy można wykorzystać stos macierzy. Stos macierzy jest obsługiwany za pomocą bezparametrowych procedur glpushmatrix() i glpopmatrix().

Stos macierzy (2) Procedura glpushmatrix powoduje skopiowanie i umieszczenie na stosie aktualnej macierzy. Procedura glpopmatrix powoduje odczytanie ze stosu macierzy i zastąpienie nią aktualnej macierzy. Na następnym slajdzie zostanie przedstawiony przykład wykorzystujący procedurę glutsolidcube. Procedura ta rysuje sześcian. Jej jedynym parametrem jest długość krawędzi.

Stos macierzy (3) Poniższy przykład rysuje obiekt złożony z wielu sześcianów: glcolor3d(1,0,0); glutsolidcube(1); glcolor3d(0,1,0); glpushmatrix(); gltranslatef(0.5,0,0); glutsolidcube(0.25); glpopmatrix(); glpushmatrix(); gltranslatef(-0.5,0,0); glutsolidcube(0.25); glpopmatrix(); glpushmatrix(); gltranslatef(0,0.5,0); glutsolidcube(0.25); glpopmatrix(); glpushmatrix(); gltranslatef(0,-0.5,0); glutsolidcube(0.25); glpopmatrix(); glpushmatrix(); gltranslatef(0,0,0.5); glutsolidcube(0.25); glpopmatrix(); glpushmatrix(); gltranslatef(0,0,-0.5); glutsolidcube(0.25); glpopmatrix();

Ćwiczenie Zastanów się jak wygląda obiekt rysowany przez kod z poprzedniego slajdu. Zastąp dotychczasowy kod rysujący torus lub inny obiekt kodem z przykładu. Czy takiego wyniku oczekiwałeś? Zastanów się jak stworzyć obiekt z poniższego rysunku:

Rysowanie dowolnych obiektów (1) Dotychczas korzystaliśmy z gotowych procedur, które rysowały całe obiekty sześciany, torusy i inne. Można jednak narysować dowolny obiekt korzystając z takich składowych jak: linie, trójkąty i czworokąty itp. Aby rysować jakiś obiekt należy przełączyć OpenGL w tryb rysowania. Służą do tego metody glbegin i glend.

Rysowanie dowolnych obiektów (2) W trybie rysowania podaje się współrzędne wierzchołków tworzących składowe obiektu (procedura glvector3d(x,y,z)). glbegin rozpoczyna tryb rysowania i posiada jeden parametr, który może przyjmować między innymi takie wartości tak: GL_POINTS każdy kolejny wierzchołek to jeden punkt GL_LINES każde dwa kolejne wierzchołki to jeden odcinek GL_TRIANGLES każde trzy kolejne wierzchołki to jeden trójkąt GL_QUADS każde cztery kolejne wierzchołki to jeden czworokąt glend wychodzi z trybu rysowania i nie posiada żadnych parametrów.

Rysowanie dowolnych obiektów (3) glcolor3f(1,1,1); glbegin(gl_lines); glvertex3d(0,4.08,0); glvertex3d(0,0,2.88); glvertex3d(0,4.08,0); glvertex3d(2.5,0,-1.44); glvertex3d(0,4.08,0); glvertex3d(-2.5,0,-1.44); glvertex3d(0,0,2.88); glvertex3d(-2.5,0,-1.44); glvertex3d(0,0,2.88); glvertex3d(2.5,0,-1.44); glvertex3d(-2.5,0,-1.44); glvertex3d(2.5,0,-1.44); glend(); glbegin(gl_triangles); glcolor3f(1,0,0); glvertex3d(0,4.08,0); glvertex3d(0,0,2.88); glvertex3d(-2.5,0,-1.44); glcolor3f(0,1,0); glvertex3d(0,4.08,0); glvertex3d(0,0,2.88); glvertex3d(2.5,0,-1.44); glcolor3f(0,0,1); glvertex3d(0,4.08,0); glvertex3d(2.5,0,-1.44); glvertex3d(-2.5,0,-1.44); glcolor3f(1,1,0); glvertex3d(2.5,0,-1.44); glvertex3d(-2.5,0,-1.44); glvertex3d(0,0,2.88); glend();

Ćwiczenie Zakomentuj kod włączający cieniowanie (włączenie cieniowania, włączenie światła i ustawienie typu cieniowania). Napisz kod, który rysuje z trójkątów sześcian (każda ściana złożona z dwóch trójkątów). Każda ściana powinna być innego koloru.

Listy operacji (1) Narysowanie skomplikowanego obiektu może trwać dużo czasu. Można jednak stworzyć prekompilowany zbiór operacji, który wykonuje się zdecydowanie szybciej. Aby stworzyć nowy zbiór operacji należy przełączyć OpenGL w odpowiedni tryb. Operacje wykonywane w tym trybie są rejestrowane, a po zamknięciu trybu kompilowane.

Listy operacji (2) Rejestrację operacji rozpoczyna się wywołaniem procedury void glnewlist( GLuint list, GLenum mode ), a kończy void glendlist(). Pierwszym parametrem glnewlist jest liczba, która jest identyfikatorem budowanej listy poleceń. Drugim parametrem jest jedna z dwóch wartości: GL_COMPILE operacje są jedynie kompilowane GL_COMPILE_AND_EXECUTE operacje są kompilowane i wykonywane

Listy operacji (3) Zbudowaną listę operacji wykonuje się za pomocą procedury void glcalllist( GLuint list ), której parametrem jest numer wywoływanej listy. Listę można usunąć za pomocą procedury void gldeletelists( GLuint list, GLsizei range ). Parametrami tej procedury są odpowiednio numer pierwszej listy i liczba kolejnych list do usunięcia.

Listy operacji (4) Poniższy kod definiuje listę operacji (wystarczy to zrobić tylko raz), a następnie wykonuje tę listę (co można robić wielokrotnie). //Utworzenie listy glnewlist(1,gl_compile); glcolor3f(1,1,1); glbegin(gl_lines); glvertex3d(0,4.08,0); glvertex3d(0,0,2.88); glvertex3d(0,4.08,0); glvertex3d(2.5,0,-1.44); glvertex3d(0,4.08,0); glvertex3d(-2.5,0,-1.44); glvertex3d(0,0,2.88); glvertex3d(-2.5,0,-1.44); glvertex3d(0,0,2.88); glvertex3d(2.5,0,-1.44); glvertex3d(-2.5,0,-1.44); glvertex3d(2.5,0,-1.44); glend(); glendlist(); //Wywołanie listy glcalllist(1);

Ćwiczenie (1) Zastąp fragment programu rysujący sześcian z poprzedniego ćwiczenia następującym kodem: float x,y; float anglestep=0.5; glbegin(gl_quads); for (x=-180;x<180;x+=anglestep) { for (y=-180;y<180;y+=anglestep) { float height1=(sin(x*3.14/180)+cos(y*3.14/180))/2; float height2=(sin((x+anglestep)*3.14/180)+cos(y*3.14/180))/2; float height3=(sin(x*3.14/180)+cos((y+anglestep)*3.14/180))/2; float height4=(sin((x+anglestep)*3.14/180)+cos((y+anglestep)*3.14/180))/2; float color1=(height1+1)/3+0.33; float color2=(height2+1)/3+0.33; float color3=(height3+1)/3+0.33; float color4=(height4+1)/3+0.33; glcolor3d(color1,color1,color1); glvertex3d(x/180,height1,y/180); glcolor3d(color2,color2,color2); glvertex3d((x+anglestep)/180,height2,y/180); glcolor3d(color4,color4,color4); glvertex3d((x+anglestep)/180,height4,(y+anglestep)/180); glcolor3d(color3,color3,color3); glvertex3d(x/180,height3,(y+anglestep)/180); } } glend();

Ćwiczenie (2) Zobacz, czy obiekt rysuje się wystarczająco szybko (zastanów się z ilu trójkątów składa się rysunek na jeden czworokąt składają się 2 trójkąty). Przenieś teraz ten kod do części inicjującej i zbuduj za jego pomocą listę operacji. W jego miejsce w procedurze rysującej klatkę umieść jedynie wywołanie listy. Porównaj szybkość rysowania klatek przed i po.

Teksturowanie (1) Teksturowaniem nazywamy nakładanie na ściany wyświetlanych brył obrazków tekstur (potocznie nazywanych po prostu "teksturami"). W OpenGL istnieją mechanizmy pozwalające na łatwe oteksturowanie modelu, ale nie ma procedur wczytujących tekstury z plików graficznych to trzeba zaimplementować samemu. Podczas ćwiczeń wykorzystamy bibliotekę do odczytu plików TGA, którą można sciągnąć z: http://gpwiki.org/index.php/loadtgacpp

Teksturowanie (2) Biblioteka definiuje klasę TGAImg, która zawiera metody takie jak: int Load(char* szfilename) wczytuje plik, zwraca wartość IMG_OK, jeśli obraz odczyta się w porządku int GetBPP() - zwraca liczbę bitów na piksel int GetWidth() - zwraca szerokość obrazka int GetHeight() - zwraca wysokość obrazka unsigned char* GetImg() - zwraca wskaźnik do danych obrazka unsigned char* GetPalette() - zwraca wskaźnik do danych palety obrazka

Teksturowanie (3) Kiedy obraz zostanie wczytany do pamięci należy go zaimportować do OpenGL. Wykonuje się to za pomocą trzech funkcji OpenGL: glgentextures() glbindtexture() glteximage2d()

Teksturowanie (4) - glgentextures Służy do inicjowania uchwytów tekstur. void glgentextures(glsizei n, GLuint *textures) n liczba uchwytów do zainicjowania textures tablica zmiennych typu Gluint, która ma przechowywać nowe uchwyty Jeżeli inicjujemy tylko jeden uchwyt można zastosować następujący kod: GLuint tex; //Uchwyt... glgentextures(1,&tex); //Zainicjuj jeden uchwyt

Teksturowanie (5) - glbindtexture Ustawia aktualny uchwyt tesktury. void glbindtexture(glenum target, GLuint texture); target typ tekstury, na naszych zajęciach zawsze GL_TEXTURE_2D texture uchwyt, który ma być aktualny

Teksturowanie (6) - glteximage2d Wczytuje obrazek do OpenGL, obrazek przechowywany w obiekcie klasy TGAImg można po wykonaniu tej procedury usunąć z pamięci. void glteximage2d( Glenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels ); target zawsze GL_TEXTURE_2D level poziom mipmapy, dla nas zawsze 0 internalformat liczba składowych koloru: Dla obrazków 32 bitowych 4 (RGBA) Dla obrazków 24 bitowych 3 (RGB)

Teksturowanie (7) glteximage2d width szerokość obrazka (potęga dwójki) height wysokość obrazka (potęga dwójki) border na zajęciach zawsze 0 format: GL_RGBA dla obrazków 32 bitowych GL_RGB dla obrazków 24 bitowych type na zajęciach GL_UNSIGNED_BYTE pixels wskaźnik do obszaru pamięci, w którym zapisano dane obrazka, można go uzyskać korzystając z metody GetImg klasy TGAImg.

Teksturowanie (8) Do załadowania pojedynczej tekstury można użyć następującego kodu: if (img.load(texname)==img_ok) { glgentextures(1,&tex); //Zainicjuj uchwyt tex glbindtexture(gl_texture_2d,tex); //Przetwarzaj uchwyt tex if (img.getbpp()==24) //Obrazek 24bit glteximage2d(gl_texture_2d,0,3,img.getwidth(),img.getheight(),0, GL_RGB,GL_UNSIGNED_BYTE,img.GetImg()); else if (img.getbpp()==32) //Obrazek 32bit glteximage2d(gl_texture_2d,0,4,img.getwidth(),img.getheight(),0, GL_RGBA,GL_UNSIGNED_BYTE,img.GetImg()); else { //Obrazek 16 albo 8 bit, takimi się nie przejmujemy } } else { //błąd }

Teksturowanie (9) - gldeletetextures Zwalnia pamięć zajmowaną przez tekstury o zadanych uchwytach. void gldeletetextures(glsizei n, GLuint *textures) n liczba uchwytów do zwolnienia textures tablica zmiennych typu Gluint, która przechowuje usuwane uchwyty

Teksturowanie (10) Aby móc używać tekstur przy rysowaniu obrazów należy jeszcze ustawić kilka parametrów teksturowania: gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_REPEAT); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_REPEAT); Powyższe parametry ustawiają zawijanie tekstur i filtrowanie dwuliniowe (bilinear filtering) bez mipmap. Powyższe ustawienia będziemy wykorzystywać podczas zajęć, ale sugeruję poeksperymentowanie z nimi w domu. Ustawienia te można zmienić w dowolnym momencie działania programu.

Teksturowanie (11) Ostatnią czynnością jaką należy wykonać przed rysowaniem teksturowanych obiektów jest włączenie teksturowania: glenable(gl_texture_2d) W trakcie pracy programu można włączać i wyłączać (gldisable) teksturowanie.

Teksturowanie (12) Aby rysowany obiekt pokryć teksturą należy z każdym jego wierzchołkiem skojarzyć współrzędne tekstury mu odpowiadające. Robi się to za pomocą procedury: gltexcoord2d(gldouble s, GLdouble t) Procedurę należy wywołać przed podaniem współrzędnych wierzchołka pomiędzy procedurami glbegin() i glend().

Teksturowanie (13) Poniższy przykład rysuje oteksturowany kwadrat: glbindtexture(gl_texture_2d,tex); //Używamy teksturę tex glbegin(gl_quads); gltexcoord2d(0,0);glvertex3f(-1,-1,0); gltexcoord2d(1,0);glvertex3f(1,-1,0); gltexcoord2d(1,1);glvertex3f(1,1,0); gltexcoord2d(0,1);glvertex3f(-1,1,0); glend();

Ćwiczenie Zmodyfikuj program z poprzednich ćwiczeń tak, aby rysował i animował teksturowany sześcian. Na potrzeby ćwiczenia wyłącz cieniowanie.

Zaawansowane cieniowanie (1) Aby OpenGL poprawnie obliczał ile światła przypada na każdą ścianę lub każdy wierzchołek bryły, musi znać normalne do powierzchni. Normalna jest wektorem który jest prostopadły do powierzchni. Normalną można obliczyć znając trzy punkty definiujące tę powierzchnię. OpenGL wymaga, aby wektory normalne miały jednostkową długość.

Zaawansowane cieniowanie (2) Punkty definiujące powierzchnię stanowią początek i 2 końce dwóch wektorów. n b a Mając dane współrzędne wektorów a i b, nieznormalizowany wektor prostopadły do płaszczyzny można znaleźć obliczając wyznacznik przedstawiony na następnym slajdzie.

Zaawansowanie cieniowanie (3) Normalną można uzyskać z następującego wyznacznika: i j k n= a x a y a z bx by bz gdzie i, j i k to wektory tworzące układ współrzędnych, odpowiednio <1,0,0>, <0,1,0> i <0,0,1>. Uwaga! Zamiana wektorów powoduje zmianę zwrotu normalnej na przeciwny!

Zaawansowane cieniowanie (4) Aby znormalizować długość wektora normalnego należy wszystkie jego współrzędne podzielić przez długość wektora. Można również włączyć automatyczną normalizację długości wektora normalnego za pomocą wywołania: glenable(gl_normalize);

Zawansowane cieniowanie (5) Współrzędne wektora normalnego podajemy podobnie jak współrzędne tekstur, pomiędzy glbegin() i glend() za pomocą funkcji glnormal3d(). Normalną można podać dla całej ściany, wywołując glnormal3d przed podaniem współrzędnych wierzchołków ściany. Normalną można również podać dla wierzchołka, wywołując glnormal3d przed podaniem współrzędnych konkretnego wierzchołka.

Zaawansowane cieniowanie (6) void glnormal3d( GLdouble nx, GLdouble ny, GLdouble nz ); nx, ny, nz współrzędne wektora normalnego Przykład: glbindtexture(gl_texture_2d,tex); //Używamy teksturę tex glbegin(gl_quads); glnormal3d(0,0,-1); //Sciana jest zwrócona w kierunku obserwatora gltexcoord2d(0,0);glvertex(0,0,0); gltexcoord2d(1,0);glvertex(10,0,0); gltexcoord2d(1,1);glvertex(10,10,0); gltexcoord2d(0,1);glvertex(0,10,0); glend();

Ćwiczenie Zmodyfikuj program z poprzedniego ćwiczenia dodając definicje wektorów normalnych ścian. Usuń instrukcję włączającą śledzenie kolorów przez ściany: glenable(gl_color_material). Włącz cieniowanie gładkie. Co zauważyłeś?

Zaawansowane cieniowanie (7) Jak można zauważyć w ćwiczeniu, pomimo tego, że włączone jest cieniowanie gładkie, każda ściana w dowolnym miejscu dostaje tyle samo światła. Wynika to z faktu, że normalna jest zdefiniowana dla całej ściany, a zatem ilość światła padającego jest obliczana na całej ścianie tak samo (światło znajduje się w nieskończoności za obserwatorem). Aby poprawić jakość cieniowania należy zdefiniować inną normalną dla każdego wierzchołka.

Zaawansowane cieniowanie (8) Matematycznie nie istnieje coś takiego jak normalna wierzchołka! W praktyce stosujemy znormalizowaną średnią arytmetyczną normalnych wszystkich ścian spotykających się w danym wierzchołku.

Ćwiczenie Zmodyfikuj poprzednie ćwiczenie definiując teraz normalną dla każdego wierzchołka, jako średnią arytmetyczną normalnych sąsiadujących ścian.

Zaawansowane cieniowanie (9) Zbiór parametrów optycznych rysowanej ściany, które są wykorzystywane we wzorze modelu oświetlenia nazywamy materiałem. W skład materiału wchodzą następujące parametry: Ambient określa w jakim stopniu odbijane jest światło otoczenia Diffuse określa w jakim stopniu odbijane jest światło rozproszone Specular określa w jakim stopniu występują odbicia lustrzane Emmision określa emitowane światło Shininess określa połyskliwość ściany

Zaawansowane cieniowanie (10) Aby ustawić parametry ambient, dissuse, specular i emission stosujemy procedurę: void glmaterialfv( GLenum face, Glenum pname, const GLfloat *param ); face określa której strony ściany dotyczy ustawiany parametr GL_FRONT, GL_BACK, GL_FRONT_AND_BACK.

Zaawansowane cieniowanie (11) pname modyfikowany parametr materiału: GL_AMBIENT GL_DIFFUSE GL_SPECULAR GL_EMISSION GL_AMBIENT_AND_DIFFUSE param wskaźnik na tablicę 4 liczb typu Glfloat z przedziału <0,1>, które definiują kolejno wpływ własności materiału na składowe światła: czerwoną, zieloną, niebieską i alfa.

Zaawansowane cieniowanie (12) Parametr shininess można zmienić za pomocą procedury: void glmaterialf( GLenum face, Glenum pname, const GLfloat param ); face tak samo jak dla glmaterialfv pname musi być GL_SHININESS param wartość wykładnika phonga (czyli połyskliwość materiału)

Ćwiczenie 1 Zmodyfikuj procedurę rysującą sześcian tak, aby ustalić wektory normalne dla każdego wierzchołka. Ustaw następujące parametry materiału: ambient i emmision {0,0,0,1} diffuse {0.7,0.5,0.5,1} specular {0.5,0.5,0.5,1} shininess 50 Zastanów się, dlaczego odbicie lustrzane można zobaczyć tylko w sytuacji, gdy wierzchołek sześcianu jest skierowany na obserwatora. Poeksperymentuj trochę z parametrami materiału.

Ćwiczenie 2 Zastąp kod rysujący sześcian z poprzedniego ćwiczenia następującym: void wall() { int subdiv=100; float back=1; float dn=(2.0/3)/subdiv; float nx=-1.0/3; float ny=-1.0/3; float dst=1.0/subdiv; float s=0; float t=0; float x=-back; float y=-back; float dp=(float)2*back/subdiv; glenable(gl_normalize); glbegin(gl_quads); for (int i1=0;i1<subdiv;i1++) { for (int i2=0;i2<subdiv;i2++) { glnormal3f(nx,ny,-1.0/3); gltexcoord2f(s,t); glvertex3f(x,y,-back); glnormal3f(nx+dn,ny,-1.0/3); gltexcoord2f(s+dst,t); glvertex3f(x+dp,y,-back); glnormal3f(nx+dn,ny+dn,-1.0/3); gltexcoord2f(s+dst,t+dst); glvertex3f(x+dp,y+dp,-back); glnormal3f(nx,ny+dn,-1.0/3); gltexcoord2f(s,t+dst); glvertex3f(x,y+dp,-back); nx+=dn;x+=dp;s+=dst; } nx=-1.0/3;x=-back;s=0; ny+=dn;y+=dp;t+=dst; } glend(); }... glpushmatrix(); wall(); glrotatef(90,1,0,0); wall(); glrotatef(90,1,0,0); wall(); glrotatef(90,1,0,0); wall(); glrotatef(90,1,0,0); glrotatef(90,0,1,0); wall(); glrotatef(180,0,1,0); wall(); glpopmatrix();

Ćwiczenie 2 (cd) Zobacz, czy teraz odbicia lustrzane są lepiej rysowane. Dlaczego?

Zaawansowane cieniowanie (13) Domyślnym ustawieniem światła jest białe światło umieszczone w nieskończoności za obserwatorem. W zależności od implementacji OpenGL można używać różnej liczby świateł. Maksymalną liczbę świateł określa stała GL_MAX_LIGHTS. Parametry świateł, takie jak: kolor, natężenie, pozycja i typ można zmodyfikować za pomocą procedur gllightf i gllightfv.

Zaawansowane cieniowanie (14) void gllightfv( GLenum light, GLenum pname, const GLfloat *params ); light stała określająca numer światła GL_LIGHTn. pname w celu określenia charakterystyki światła pname przyjmuje wartości GL_AMBIENT, GL_DIFFUSE i GL_SPECULAR, wówczas params jest wektorem czterech liczb (RGBA), określających kolor danego rodzaju światła.

Zaawansowane cieniowanie (15) Punktowe źródło światła: Aby określić położenie światła, również wykorzystywane jest polecenie gllightfv. Jako pname należy tutaj podać GL_POSITION, a jako param podajemy wektor 4 (x,y,z,w) liczb określających położenie światła we współrzędnych homogenicznych. Współrzędne światła są przekształcane przez macierz model - widok (są traktowane jak punkt w przestrzeni obiektu) i zapamiętywane w przestrzeni oka. Jeżeli wartość w wektora położenia jest równa zero, to pozostałe wartości są traktowane jako wektor określający kierunek światła, a samo światło jest umieszczane w nieskończoności wzdłuż wektora.

Zaawansowane cieniowanie (16) Jasność punktowego źródła światła może maleć wraz z odległością. Matematycznie jest to wyrażone przez współczynnik: umieszczony przed jasnością światła w modelu oświetlenia. Parametry kc, kl i kq można ustawić za pomocą procedury gllightf podając jako pname odpowiednio GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION i GL_QUADRATIC_ATTENUATION, a odpowiednią wartość jako param

Ćwiczenie Umieść w procedurze rysującej, przed rysowaniem sześcianu z poprzedniego ćwiczenia następujące instrukcje: float lightpos[]={0,0,-1,0}; gllightfv(gl_light0,gl_position,lightpos); Poobracaj trochę tym sześcianem i zaobserwuj różnicę w zachowaniu oświetlenia w stosunku do poprzedniego ćwiczenia. Zastanów się jak można wytłumaczyć to, że światło się obraca wraz z sześcianem, oraz to, że mimo tego odbicie lustrzane pozostaje w miejscu.

Zaawansowane cieniowanie (17) Stożkowe źródło światła: Punktowe źródło światła, ale światło nie rozchodzi się w każdym kierunku jednakowo.

Zaawansowane cieniowanie (18) Kierunek stożka określa się za pomocą procedury gllightfv podając jako pname GL_SPOT_DIRECTION, a wektor reprezentujący kierunek jako param (we współrzędnych homogenicznych). Połowę kąta rozwarcia stożka określa się za pomocą procedury gllightf podając jako pname GL_SPOT_CUTOFF, a jako param liczbę z zakresu <0,90> lub 180 (co oznacza, światło punktowe) Stopień skupienia światła w stożku określa się za pomocą procedury gllightf podając jako pname GL_SPOT_EXPONENT, a jako param liczbę z zakresu <0,128>. Im większa wartość tym większe skupienie, 0 oznacza równomierne rozmieszczenie światła.

Ćwiczenie Usuń linijki kodu ustawiające pozycję światła dodane w poprzednim ćwiczeniu. Ustaw właściwości materiału sześcianu tak, aby nie dawał on odbić lustrzanych. Do części inicjującej, zaraz po załadowaniu do macierzy model-widok macierzy jednostkowej, dodaj kod ustawiający źródło światła stożkowego GL_LIGHT0 w pozycji <0,0,3>, o kącie rozwarcia 20 stopni (GL_SPOT_CUTOFF 10), skierowane w kierunku <0,0,-1>. Uruchom program, zaobserwuj efekt działania oświetlenia. Następnie dodaj linijkę ustawiającą skupienie światła na maksimum (GL_SPOT_EXPONENT 128) i znowu uruchom program.

Zaawansowane cieniowanie (19) Za pomocą procedur gllightmodelf() i gllightmodelfv() można modyfikować pewne dodatkowe parametry modelu oświetlenia. Tutaj przeanalizujemy tylko jeden z nich. Za pomocą gllightmodelfv() można na przykład zmienić rozproszone światło otoczenia (ambient). gllightmodelfv(gl_light_model_ambient, params), gdzie params, to wektor czterech liczb (RGBA) określających kolor światła otoczenia.

Ćwiczenie Do wyniku poprzedniego ćwiczenia, w części inicjującej, dodaj linijkę ustawiającą światło otoczenia na <1,1,1,1> i zmodyfikuj materiał sześcianu tak, aby odbijał światło otoczenia następująco <0.1,0.1,0.1,1>.

Systemy cząstek (1) Systemy cząstek (particle systems) są sposobem modelowania pewnych obiektów o nie zdefiniowanym do końca kształcie, takich jak: woda, dym, chmury, ogień. Pozwalają na stworzenie realistycznych obrazów i animacji niskim nakładem pracy. Są wykorzystywane od dawna w grafice komputerowej, zarówno w grach komputerowych (najczęściej eksplozje), jak i filmach, np. wybuch bomby Genesis w "Star Trek II: Gniew Khana".

Systemy cząstek (2) Od "normalnych obiektów" obiekty modelowane za pomocą systemów cząstek różnią się w przynajmniej trzech aspektach: Nie są reprezentowane za pomocą wielokątów, ale chmur cząstek. Chmura cząstek nie ma z góry zdefiniowanego kształtu. Kształt wynika z reguł rządzących zachowaniem cząstek. Zachowanie i wygląd obiektów modelowanych za pomocą systemów cząstek nie jest deterministyczny i jest modelowany za pomocą procesów losowych.

Systemy cząstek (3) Modelowane zjawisko jest reprezentowane jako zbiór cząstek, z których każda posiada przynajmniej : swoje położenie, wektor prędkości. Najczęściej jednak dodatkowo przechowuje się takie parametry, jak: rozmiar, kolor, przezroczystość, czas życia, kształt, masę, ładunek itp. Dodatkowo podczas animacji uwzględnia się dodatkowe parametry, np: grawitacja, pole magnetyczne itp.

Systemy cząstek (4) W każdej kolejnej rysowanej klatce położenie i wektor prędkości każdej cząstki są modyfikowane przy uwzględnieniu następujących parametrów (niekoniecznie wszystkich): aktualnego wektora prędkości (położenie), grawitacji, pola magnetycznego, pola elektrostatycznego, sił odśrodkowych, kolizji z obiektami na scenie (wektor prędkości), interakcji pomiędzy cząstkami się raczej nie modeluje.

Systemy cząstek (5) Każda cząstka ma pewien czas życia, bądź inny warunek śmierci. Kiedy cząstka "zginie", a mamy do czynienia z ciągłym zjawiskiem (np. emisja dymu), to powinna być ponownie zainicjowana (nowe położenie, nowy wektor prędkości, nowy kolor itd. - coś jak reinkarnacja :)) W przeciwnym wypadku (np. eksplozja), powinna po prostu przestać być przetwarzana i rysowana.

Systemy cząstek (6) Podstawowy schemat przetwarzania systemów cząstek wygląda następująco: 1) Generowane są nowe cząstki, 2) Każdej cząstce przypisywany jest własny zestaw wartości parametrów, 3) Stare cząstki są niszczone, 4) Pozostałe cząstki są modyfikowane zgodnie z wszystkimi parametrami modelu, 5) Cząstki są rysowane

Systemy cząstek (7) - Rysowanie W najprostszy sposób można narysować cząstkę jako punkt. Zaletą tego podejścia jest: szybkość i łatwość implementacji. Wadą jest słaba jakość uzyskanych obrazów. Sposób ten jednak nadaje się całkiem nieźle do modelowania niektórych zjawisk: śnieg, iskry, burza piaskowa.

Ćwiczenie (1) Korzystając ze szkieletu programu ze strony www.cs.put.poznan.pl/wandrzejewski/ napisz program symulujący eksplozję za pomocą systemu cząstek. Szkielet został przygotowany do napisania prostego programu w OpenGL, animującego system cząstek, który reaguje na klawiaturę. Strzałki obracają układ współrzędnych, F1 wł/wył pauzy, F2 ponowne zainicjowanie Czytaj dalej cząstek.

Ćwiczenie (2) 1.Rozpocznij od zdefiniowania struktury reprezentującej cząstkę. Struktura powinna zawierać pola reprezentujące położenie cząstki, jej wektor prędkości, czas życia i kolor. struct Particle { double x,y,z; //Położenie cząstki double vx,vy,vz; //Wektor prędkości cząstki double r,g,b; //Kolor cząstki int TTL; //Czas życia cząstki }; Czytaj dalej

Ćwiczenie (3) 2.Zdefiniuj stałą określającą liczbę symulowanych cząstek oraz tablicę zmiennych reprezentujących cząstki. #define NUM_PARTICLES 700 struct Particle particles[num_particles]; Czytaj dalej

Ćwiczenie (4) 3.Napisz procedurę initparticle(int i) inicjującą pojedynczą cząstkę, o zadanym numerze w tablicy. Początkowe położenie cząstki ustal na <0,0,0>. Współrzędne wektora vx, vy, vz prędkości ustal losowo na wartości z przedziału <-3,3>. Czas życia cząstki ustal na losową wartość z przedziału <5,25>. Składową niebieską koloru cząstki ustal na 0, czerwoną na 1 a zieloną na losową wartość z przedziału <0.4,0.6>. Czytaj dalej

Ćwiczenie (5) 4.Napisz procedurę inicjującą wszystkie cząstki w pętli. void initparticles() { for (int i=0;i<num_particles;i++) { //Dla każdej cząstki initparticle(i); //Nadaj jej początkowe wartości } } Czytaj dalej

Ćwiczenie (6) 5.Uzupełnij procedurę nextframe tak, aby modyfikowała współrzędne cząstek zgodnie z ich prędkością, oraz zmniejszała czas ich życia. for (int i=0;i<num_particles;i++) { if (particles[i].ttl>0) { particles[i].x+=particles[i].vx; particles[i].y+=particles[i].vy; particles[i].z+=particles[i].vz; particles[i].ttl--; } } Czytaj dalej

Ćwiczenie (7) 6.Uzupełnij procedurę displayframe tak, aby rysowała wszystkie cząstki jako punkty o odpowiednim kolorze. Rysowane powinny być tylko cząstki o czasie życia większym od zero. 7.Zaobserwuj jak wygląda chmura cząstek pauzując ją zanim wszystkie cząstki zginą. Czy chmura wygląda zadowalająco? Zastanów się dlaczego zanim zaczniesz wykonywać dalszy ciąg ćwiczenia. Czytaj dalej

Ćwiczenie (8) 8.Chmura cząstek ma kształt sześcianu. Wynika to z nieodpowiedniego przygotowania początkowych wektorów prędkości. Aby wygenerować odpowiednie wartości należy znormalizować wszystkie wylosowane wektory prędkości, a następnie pomnożyć wszystkie współrzędne tych wektorów razy tą samą wartość (dla każdej cząstki inną). Przykładowy kod generujący początkowe wartości wektorów prędkości umieszczono na następnym Czytaj dalej slajdzie.

Ćwiczenie (9) float vx,vy,vz,l; vx=my_rand()-0.5; vy=my_rand()-0.5; vz=my_rand()-0.5; l=sqrt(vx*vx+vy*vy+vz*vz); l=l/(my_rand()+0.3)/5; vx=vx/l; vy=vy/l; vz=vz/l; particles[i].vx=vx; particles[i].vy=vy; particles[i].vz=vz; gdzie my_rand() jest funkcją zwracającą Czytaj dalej pseudolosową wartość z przedziału <0,1>.

Ćwiczenie (10) 9.Uruchom program po wprowadzeniu poprawek. Zaobserwuj jaki kształt ma obecnie chmura cząstek. 10.Obecnie chmura symuluje wygląd eksplozji w przestrzeni kosmicznej (brak atmosfery i grawitacji). Wprowadźmy zatem grawitację. 11.Zdefiniuj stałą reprezentującą przyspieszenie grawitacyjne. Uwaga stała powinna być w jednostkach odpowiadających częstotliwości Czytaj dalej wywoływania nextframe. #define GRAVITY 0.50

Ćwiczenie (11) 12.Zmodyfikuj procedurę nextframe tak, aby modyfikowała wektor prędkości zgodnie z zadanym przyspieszeniem: particles[i].vy-=gravity;

Systemy cząstek (8) - Rysowanie Cząstki rysowane jako punkty nie wyglądają najlepiej. Można ich wygląd poprawić rysując cząstki za pomocą teksturowanych, półprzeźroczystych kwadratów. Aby było możliwe rysowanie przeźroczystych tekstur, należy włączyć "mieszanie" kolorów (blending). (glenable(gl_blend)). Dodatkowo należy ustalić sposób łączenia kolorów za pomocą funkcji glblendfunc.

Tryb łączenia kolorów (1) Jeżeli OpenGL jest w trybie łączenia kolorów, to kolor piksela, jaki zapisywany jest do bufora kolorów zależy zarówno od koloru jaki chcemy narysować, jak i od koloru jaki już znajduje się w buforze. Kolory łączone są w następujący sposób (zakładamy, że składowe kolorów są w przedziale od <0,1>): r=min(1,newr*snewr+oldr*soldr) g=min(1,newg*snewg+oldg*soldg) b=min(1,newb*snewb+oldb*soldb) gdzie sx to współczynniki z przedziału <0,1>, newx to składowa która jest aktualnie rysowana, a oldx to składowa w buforze kolorów.

Tryb łączenia kolorów (2) Sposób obliczania współczynników sx do wzorów na poprzednim slajdzie ustala się za pomocą procedury : glblendfunc (GLenum sfactor, Glenum dfactor). Parametr sfactor określa sposób obliczania współczynników snewx a dfactor soldx. Parametry te mogą przyjmować wiele wartości określonych stałymi dokładnie opisanymi w dokumentacji.

Tryb łączenia kolorów (3) W naszych ćwiczeniach parametr sfactor będzie miał wartość GL_SOURCE_ALPHA, a dfactor GL_ONE_MINUS_SRC_ALPHA. Oznacza to, że współczynnikiem przed nowo rysowanym kolorem będzie wartość jego składowej alfa, a kolor w buforze kolorów będzie dopełnieniem (1-alfa). Dla wartości alfa=1 nowo rysowany kolor całkowicie przykryje kolor w buforze kolorów, a dla alfa=0, nowy kolor w ogóle nie zostanie narysowany.

Systemy cząstek (9) - Rysowanie Aby zatem cząstki były rysowane ładniej, należy: Włączyć mieszanie kolorów. Przygotować teksturę, która jest cała wypełniona kolorem białym, ale składowa alfa się zmienia w ten sposób, że na środku obrazka tekstury jest równa jeden i maleje do zera w kierunku krawędzi. Zastąpić rysowanie cząstek jako punktów, rysowaniem kwadracików pokrytych naszą teksturą, o środku równemu pozycji cząstki. Powyższe rozwiązanie ma drobny problem, który zademonstruje następne ćwiczenie.

Ćwiczenie (1) Włącz mieszanie kolorów poprzez umieszczenie w części inicjującej programu następujących linii: glenable(gl_blend); glblendfunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Włącz teksturowanie i zadeklaruj zmienną, która będzie przechowywać identyfikator tekstury. GLuint tex;... int main(int argc, char* argv[]) {... glenable(gl_texture_2d);... glutmainloop(); } Czytaj dalej

Ćwiczenie (2) Wygeneruj teksturę zgodnie z opisem na poprzednich slajdach i ustal parametry teksturowania: void generatetex() { unsigned char particle_tex[64*64*4]; for (int x=-32;x<32;x++) { for (int y=-32;y<32;y++) { float color=max(0,32-sqrt((long double)x*x+y*y))*255/32; particle_tex[(x+32)*4+(y+32)*64*4]=255; particle_tex[(x+32)*4+1+(y+32)*64*4]=255; particle_tex[(x+32)*4+2+(y+32)*64*4]=255; particle_tex[(x+32)*4+3+(y+32)*64*4]=(char)color; } } glgentextures(1,&tex); glbindtexture(gl_texture_2d,tex); glteximage2d(gl_texture_2d,0,4,64,64,0,gl_rgba,gl_unsigned_byte,particle_tex); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER,GL_LINEAR); gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S,GL_REPEAT); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T,GL_REPEAT); } Powyższa procedura powinna być wywołana w funkcji main przed glutmainloop(); Czytaj dalej

Ćwiczenie (3) Zmodyfikuj procedurę displayframe tak, aby rysowała wszystkie cząstki jako kwadraty pokryte teksturą. Współrzędne wierzchołków kwadratów oblicz poprzez dodanie albo odjęcie od współrzędnych x i y cząstki stałej wartości (najlepiej zadeklaruj tą wartość jaką stałą, bądź zmienną przyda się później). Nie zapomnij włączyć GL_COLOR_MATERIAL, aby rysowane cząstki miały odpowiedni kolor. Prócz składowych r,g i b koloru ustawiaj również składową alfa w zależności od czasu życia cząstki (im cząstka ma szybciej umrzeć tym jest bardziej przeźroczysta). Czytaj dalej Z bufor musi być wyłączony. Uruchom program i zobacz jak wygląda eksplozja teraz.

Ćwiczenie (4) Spróbuj zatrzymać animację klawiszem F1 i poobracać trochę układem współrzędnych. Co zauważyłeś?

Systemy cząstek (10) - Rysowanie Jak można łatwo zauważyć, wszystkie cząstki są rysowane w jednej płaszczyźnie, która niekoniecznie musi być prostopadła do wektora wzdłuż którego patrzy obserwator. Aby kwadraciki obracały się zawsze w kierunku obserwatora należy zastosować drobny trik. Współrzędne kwadracików reprezentujących cząstki muszą być obliczone w układzie współrzędnych, w którym zawsze znamy wektor wzdłuż którego patrzy obserwator.

Systemy cząstek (11) - Rysowanie Taki układ współrzędnych to przestrzeń oka, którym obserwator znajduje się w początku układu współrzędnych i patrzy w kierunku malejących wartości współrzędnej z. W takim układzie współrzędnych, sposób obliczania współrzędnych kwadracików, zastosowany w ostatnim ćwiczeniu, będzie zawsze poprawny.

Systemy cząstek (12) - Rysowanie Niestety jest drobny problem ze wsparciem takiego rysowania przez OpenGL. Każdy wierzchołek, który jest podawany za pomocą procedury glvector() jest mnożony przez macierz model-widok w celu przekształcenia do przestrzeni oka. Aby narysować nasze cząstki chcemy, przez macierz model-widok, pomnożyć jedynie współrzędne cząstek, a wierzchołki kwadracików obliczać później, nie pozwalając OpenGL na ich modyfikację.

Systemy cząstek (13) - Rysowanie Należy zatem za pomocą procedury glgetfloatv odczytać aktualną macierz model-widok, a następnie ją zapamiętać za pomocą glpushmatrix. Następnie, aby uniknąć modyfikacji współrzędnych kwadracików, należy do macierzy model-widok załadować macierz jednostkową. Podczas rysowania należy ręcznie wykonać mnożenie współrzędnych cząstek przez macierz model-widok, a potem obliczyć współrzędne kwadratów. Po zakończeniu rysowania cząstek należy przywrócić macierz model-widok za pomocą glpopmatrix.

Systemy cząstek (14) - Rysowanie Procedura glgetfloatv służy do odczytywania wektorów liczbowych, które są parametrami różnych operacji wykonywanych przez OpenGL. glgetfloatv(glenum pname, GLfloat * params) pname to stała określająca które liczby powinny zostać odczytane. params to wskaźnik do bufora pamięci, do którego mają być zapisane odczytywane liczby. Należy zadbać, aby bufor był dostatecznie duży.

Systemy cząstek (15) - Rysowanie Jeżeli chcemy odczytać macierz model-widok należy przygotować bufor 16 liczb typu float. Jako pname podajemy GL_MODELVIEW_MATRIX, a jako params utworzony wcześniej bufor. Zwrócona macierz jest ułożona kolumnami (jeżeli stosujemy notację z wektorami pionowymi, które mnożymy lewostronnie przez macierze).

Ćwiczenie (1) Do struktury definiującej cząstkę dodaj trzy pola: nx,ny i nz typu double, które będą przechowywać współrzędne cząstki przemnożone przez macierz model-widok. Nie jest to w tej chwili konieczne, ale przyda się w dalszej części ćwiczeń. W procedurze displayframe, po wyczyszczeniu ekranu i ewentualnych modyfikacjach macierzy model-widok, odczytaj macierz model-widok za pomocą glgetfloatv. GLfloat m[16]; glgetfloatv (GL_MODELVIEW_MATRIX, m); Czytaj dalej

Ćwiczenie (2) Następnie zapisz tą macierz na stos i załaduj do niej macierz jednostkową. glmatrixmode(gl_modelview); glpushmatrix(); glloadidentity(); W kolejnym kroku oblicz współrzędne cząstek w przestrzeni oka. for (int i=0;i<num_particles;i++) { float nx,ny,nz,nw, px,py,pz; px=particles[i].x; py=particles[i].y; pz=particles[i].z; //współrzędna w=1 nx=px*m[0]+py*m[4]+pz*m[8]+m[12]; ny=px*m[1]+py*m[5]+pz*m[9]+m[13]; nz=px*m[2]+py*m[6]+pz*m[10]+m[14]; nw=px*m[3]+py*m[7]+pz*m[11]+m[15]; particles[i].nx=nx/nw; particles[i].ny=ny/nw; particles[i].nz=nz/nw; } Czytaj dalej

Ćwiczenie (3) Wykorzystując obliczone wcześniej pozycje cząstek narysuj je w analogiczny sposób jak w poprzednim ćwiczeniu. Po narysowaniu cząstek, przywróć oryginalną macierz model-widok za pomocą glpopmatrix(). Uruchom program i zobacz, czy tym razem można zobaczyć poprawnie rysowane cząstki. Czytaj dalej

Ćwiczenie (4) Jeżeli ćwiczenie zostało dotychczas wykonane poprawnie, to cząstki powinny być rysowane zgodnie z oczekiwaniami. Ponieważ prócz systemów cząstek rysujemy często również inne obiekty na scenie konieczne jest włączenie Z-bufora. Spróbuj zatem włączyć Z-bufor (glenable(gl_depth_test)) i uruchomić program. Czy wszystko rysuje się poprawnie? Czytaj dalej

Ćwiczenie (5) Po włączeniu Z-bufora otrzymuje się następujący wynik:

Systemy cząstek (16) - Rysowanie Problem jaki można było zaobserwować na ostatnim ćwiczeniu wynika z faktu, że w Z-buforze nie jest uwzględniana przeźroczystość rysowanych obiektów. W Z-buforze, pomimo tego, że w buforze kolorów tego nie widać, rysowane są całe kwadraty. Zatem, jeżeli zdarzy się, że cząstka znajdująca się blisko obserwatora zostanie narysowana pierwsza, to kolejne cząstki, które znajdują się za nią nie zostaną w ogóle narysowane, pomimo tego, że mogłyby być częściowo widoczne.

Systemy cząstek (17) - Rysowanie Problem ten występuje zawsze tam, gdzie stosuje się mieszanie kolorów w celu uzyskania przeźroczystości. Jedynym sposobem uzyskania poprawnego wyniku, jest narysowanie cząstek w kolejności od znajdującej się najdalej od obserwatora, do znajdującej się najbliżej. Należy zatem przed narysowaniem posortować cząstki według ich współrzędnej z w przestrzeni oka.

Ćwiczenie (1) Zadeklaruj tablicę wskaźników na zmienne typu Particle, o liczbie komórek równej liczbie cząstek. struct Particle* particleorder[num_particles]; W części inicjującej zapisz do tej tablicy wskaźniki na kolejne komórki w tablicy particles. for (int i=0;i<num_particles;i++) { particleorder[i]=&particles[i]; } Sortowaniu będzie podlegać tablica particleorder, gdyż zawiera mniejsze elementy, a zatem sortowanie będzie zachodziło szybciej. Czytaj dalej

Ćwiczenie (2) Do sortowania wykorzystamy funkcję qsort dostępną standardowo w języku C. Funkcja qsort jest ogólna, i działa na tablicach dowolnych elementów. Konieczne jest jednak zdefiniowanie funkcji porównującej dwa elementy: int compare( const void* elem1, const void* elem2 ) Funkcja powinna zwracać wartość ujemną, jeżeli element 1 jest mniejszy od drugiego, 0 jeśli elementy są równe i wartość dodatnią Czytaj dalej w przeciwnym wypadku.

Ćwiczenie (3) Przed funkcją displayframe zdefiniuj funkcję compare. int compare( const void* elem1, const void* elem2 ) { float d=(*(struct Particle**)elem1)->nz-(*(struct Particle**)elem2)->nz; if (d<0) return -1; //Ponieważ compare zwraca int, to nie if (d>0) return 1; //można zwracać bezpośrednio różnicy. return 0; } Po obliczeniu współrzędnych cząstek w przestrzeni oka, posortuj wskaźniki w tablicy particleorder według współrzędnej z (pole nz). qsort(particleorder,num_particles,sizeof(struct Particle*),compare); Zmodyfikuj kod rysujący cząstki tak, aby rysował je według kolejności w tablicy particleorder. Czytaj dalej

Ćwiczenie (4) Program powinien być wolny od pokazanego wcześniej problemu.

Systemy cząstek (18) - Rysowanie Z cząstkami rysowanymi w pokazany przed chwilą sposób związany jest jeszcze jeden problem. Chociaż nie musi być już rozwiązywany, może w niektórych sytuacjach przeszkadzać. Współrzędne wierzchołków kwadratów reprezentujących cząstki nie są mnożone przez macierz model-widok, i przez to nie są poprawnie skalowane. Problemu tego nie da się rozwiązać w sposób dokładny, ponieważ deformacji kulistych kształtów, które nasze kwadraty mają symulować, nie da się dokładnie oddać za pomocą deformacji kwadratów.

Systemy cząstek (19) - Rysowanie Jeżeli skalowanie nie powoduje dużych deformacji proporcji, to można zastosować następujący, uproszczony sposób. Należy obliczyć ile wynosi w przestrzeni oka długość odcinka, który w przestrzeni modelu ma długość 1 i znajduje się na jednej z osi układu współrzędnych. Długość ta powinna być współczynnikiem skalującym długość boku rysowanych kwadratów. Można również wykorzystać średnią długości odcinków leżących na różnych osiach, albo największą lub najmniejszą wartość z tych długości.

Systemy cząstek (20) - Rysowanie Jeżeli przyjąć za początek wektora, którego długość należy obliczyć, początek układu współrzędnych, a za koniec punkt o współrzędnych <1,0,0>, to po kilku prostych przekształceniach można dojść do następującego wzoru na długość odcinka (aij to wartości z macierzy model-widok): a11 a14 a14 wx= a 41 a 44 a 44 a 21 a 24 a 24 wy= a 41 a 44 a 44 a 31 a 34 a 34 wz= a 41 a 44 a 44 d = wx 2 wy 2 wz 2

Ćwiczenie (1) Uruchom program i zatrzymaj animację eksplozji, kiedy cząstki są jeszcze dość gęsto rozmieszczone. Zacznij powiększać obraz za pomocą klawisza PGUP. Zwróć uwagę na sposób skalowania cząstek. Następnie w programie, po pętli wyliczającej współrzędne cząstek w przestrzeni oka, umieść kod obliczając długość odcinka o długości 1 zgodnie ze wzorami na ostatnich slajdach. float d,nx,ny,nz,nw; nx=m[0]+m[12]; ny=m[1]+m[13]; nz=m[2]+m[14]; nw=m[3]+m[15]; nx=nx/nw-m[12]/m[15]; ny=ny/nw-m[13]/m[15]; nz=nz/nw-m[14]/m[15]; d=sqrt(nx*nx+ny*ny+nz*nz); Czytaj dalej

Ćwiczenie (2) Pomnóż długości boków rysowanych kwadratów razy obliczony współczynnik (zmienna d). Uruchom znowu program i porównaj sposób skalowania cząstek z poprzednim wykonaniem.