Bartosz Bazyluk GRAFIKA CZASU RZECZYWISTEGO Interakcja, ruch kamery, oświetlenie. Grafika komputerowa i wizualizacja, Bioinformatyka S1, II Rok
Kamera w OpenGL Aby opisać jednoznacznie położenie kamery, należy określić: Pozycję p Kierunek widzenia d Wektor pionu u Każdy z tych elementów w przestrzeni 3D jest 3-elementowym wektorem u d p 25.04.13 Bartosz Bazyluk 2
Kamera w OpenGL Aby łatwo uzyskać w OpenGL macierz transformacji odpowiadającą kamerze umieszczonej w zadanym punkcie, patrzącą w zadanym kierunku i o zadanym wektorze pionu, najlepiej posłużyć się funkcją: glulookat( eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz ) eye* - współrzędne XYZ punktu zaczepienia kamery lookat* - współrzędne XYZ punktu, na który ma spoglądać kamera up* - współrzędne XYZ wektora pionu W dalszych rozważaniach dla uproszczenia przyjmiemy, że wektor pionu zawsze będzie równy (0;1;0) 25.04.13 Bartosz Bazyluk 3
Kamera w OpenGL Należy zwrócić uwagę, że glulookat() nie przyjmuje kierunku widzenia kamery, a punkt na który kamera ma spoglądać Jest to szczególnie istotne, gdy naszym zadaniem jest zaprogramowanie interaktywnej kamery pierwszoosobowej W takim przypadku dużo wygodniej jest przechowywać kierunek widzenia jako wektor jednostkowy Na podstawie pozycji kamery o raz kierunku można łatwo uzyskać współrzędne punktu, który można przekazać do glulookat(). p d 25.04.13 Bartosz Bazyluk 4
Kamera w OpenGL Należy zwrócić uwagę, że glulookat() nie przyjmuje kierunku widzenia kamery, a punkt na który kamera ma spoglądać Jest to szczególnie istotne, gdy naszym zadaniem jest zaprogramowanie interaktywnej kamery pierwszoosobowej W takim przypadku dużo wygodniej jest przechowywać kierunek widzenia jako wektor jednostkowy Na podstawie pozycji kamery o raz kierunku można łatwo uzyskać współrzędne punktu, który można przekazać do glulookat(). p d 25.04.13 Bartosz Bazyluk 5
Kamera w OpenGL Należy zwrócić uwagę, że glulookat() nie przyjmuje kierunku widzenia kamery, a punkt na który kamera ma spoglądać Jest to szczególnie istotne, gdy naszym zadaniem jest zaprogramowanie interaktywnej kamery pierwszoosobowej W takim przypadku dużo wygodniej jest przechowywać kierunek widzenia jako wektor jednostkowy Na podstawie pozycji kamery o raz kierunku można łatwo uzyskać współrzędne punktu, który można przekazać do glulookat(). d p+d p 25.04.13 Bartosz Bazyluk 6
Ruch kamery Zadanie: Stworzyć interaktywną kamerę pierwszoosobową Możliwości ruchu: Krok w przód/tył Krok w lewo/prawo (prostopadle do kierunku przód/tył) Obrót w jednej płaszczyźnie (wokół osi y) Sterowanie za pomocą klawiatury W/S/A/D ruch Q/E obrót 25.04.13 Bartosz Bazyluk 7
Stan kamery Reprezentacja stanu kamery w pamięci Konieczne jest zapamiętanie trzech wektorów 3-elementowych: Położenia Kierunku Pionu Sugerowane jest utworzenie struktury przechowującej trzy składowe XYZ: struct vec3 { float x, y, z; }; Wówczas stan kamery można przedstawić następująco: struct SCameraState { vec3 pos; vec3 dir; vec3 up; }; 25.04.13 Bartosz Bazyluk 8
Stan kamery Jako że dla uproszczenia przyjęliśmy, że wektor pionu jest stały, możemy go pominąć Przydatne może okazać się przechowanie aktualnej prędkości przesuwania kamery Np. płynne wygaszanie ruchu Zatem struktura stanu kamery może przyjąć następującą postać: struct SCameraState { vec3 pos; vec3 dir; float speed; }; Na potrzeby zadania utworzymy globalną instancję tej struktury i nazwiemy ją player SCameraState player; 25.04.13 Bartosz Bazyluk 9
Stan kamery W takiej sytuacji, możemy każdorazowo zasilać wywołanie glulookat() następującymi danymi: glulookat( player.pos.x, player.pos.y, player.pos.z, player.pos.x + player.dir.x, player.pos.y + player.dir.y, player.pos.z + player.dir.z, 0, 1, 0 ); 25.04.13 Bartosz Bazyluk 10
Obsługa klawiatury Aktualizacja stanu kamery powinna odbywać się w odpowiedzi na wciśnięcie klawiszy przez użytkownika Jak dowiedzieć się, kiedy użytkownik wciska, a kiedy puszcza dane klawisze? GLUT oferuje dostęp do zdarzeń, które związane są z klawiaturą Poprzez ustawienie własnych callbacków jako obsługi tych zdarzeń, możemy odpowiedzieć na ich wystąpienie Z naszego punktu widzenia istotne będą: glutkeyboardfunc() zachodzi, gdy zostanie wciśnięty klawisz glutkeyboardupfunc() - zachodzi, gdy zostanie puszczony klawisz Funkcje obsługujące oba te zdarzenia powinny przyjmować następujące trzy parametry: unsigned int key kod ASCII klawisza int x, int y położenie kursora myszy w momencie zdarzenia 25.04.13 Bartosz Bazyluk 11
Obsługa klawiatury glutkeyboardfunc() zachodzi: Natychmiastowo* w momencie wciśnięcia klawisza Jest powtarzane tak długo, jak jest wciśnięty klawisz Pierwsze powtórzenie po opóźnieniu Częstotliwość kontrolowana przez BIOS/system operacyjny Jeśli bezpośrednio użyjemy tego zdarzenia do kontroli ruchu kamery, to w momencie przytrzymania klawisza kamera zrobi skok do przodu, zatrzyma się i później ruszy dalej Opóźnienie po pierwszy kroku jest niedopuszczalne! Częstotliwość ruchów różna na różnych komputerach! glutkeyboardupfunc() - zachodzi: Natychmiastowo* w momencie puszczenia klawisza * Natychmiastowo oznacza najbliższy nadchodzący moment przetwarzania komunikatów. (wciśnięcie) (puszczenie) t 25.04.13 Bartosz Bazyluk 12
Obsługa klawiatury Jak rozwiązać problem opóźnienia i częstotliwości glutkeyboardfunc()? Tablica przechowująca aktualny stan każdego klawisza (znaku ASCII) Wartości boolowskie: false gdy puszczony, true gdy wciśnięty Gdy glutkeyboardfunc(), wtedy ustawiamy odpowiadający element tablicy na true Gdy glutkeyboardupfunc(), wtedy ustawiamy na false bool keystate[256]; glutkeyboardfunc(onkeypress); glutkeyboardupfunc(onkeyup); void OnKeyPress(unsigned char key, int x, int y) { keystate[key] = true; } void OnKeyUp(unsigned char key, int x, int y) { keystate[key] = false; } 25.04.13 Bartosz Bazyluk 13
Obsługa klawiatury Problem częstotliwości najłatwiej można rozwiązać wykonując aktualizację stanu ze stałym krokiem czasowym Podczas takiej aktualizacji sprawdzamy w tablicy klawiszy, czy nie są wciśnięte te odpowiedzialne za ruch kamery Jeśli są, dokonujemy zmian ustawienia kamery Krok czasowy w prostej implementacji może być zbliżony do 1/60s, czyli 16,(6)ms GLUT oferuje możliwość ustawienia zdarzenia, które zajdzie za zadany okres czasu gluttimerfunc(unsigned int time, void(*callback) (int), int value) Jedno przypisanie obsługi tego zdarzenia wykona się tylko raz gluttimerfunc(16, OnTimer, 0); void OnTimer(int value) { gluttimerfunc(16, OnTimer, 0); // Tutaj aktualizacja stanu } 25.04.13 Bartosz Bazyluk 14
Obsługa klawiatury Dodatkowo, przydatne jest wprowadzenie własnego, wirtualnego "zdarzenia" wywoływanego tylko raz gdy zostanie wciśnięty klawisz Nazwijmy je OnKeyDown Będzie wyzwalane w OnKeyPress, ale tylko jeśli wcześniej dany klawisz nie był wciśnięty void OnKeyDown(unsigned char key) { //... } void OnKeyPress(unsigned char key, int x, int y) { if (!keystate[key]) { OnKeyDown(key); } keystate[key] = true; } Takie zdarzenie jest wygodne do obsługi rzeczy, które włącza/wyłącza się danym klawiszem 25.04.13 Bartosz Bazyluk 15
Ruch kamery w przód/tył Aby zrealizować taki ruch, trzeba zmodyfikować położenie punktu zaczepienia kamery "Przód" w danym momencie odpowiada wektorowi kierunku kamery Pozycję po przesunięciu obliczamy w prosty sposób: p ' = p+ speed d p p' d d p p' Mała prędkość (mnożnik) Duża prędkość (mnożnik) 25.04.13 Bartosz Bazyluk 16
Ruch kamery w przód/tył Aby zrealizować poruszanie się w przód, w funkcji aktualizującej stan kamery, modyfikujemy pozycję: void OnTimer(int value) { gluttimerfunc(16, OnTimer, 0); if (keystate['w']) { player.pos.x += player.dir.x * player.speed; player.pos.y += player.dir.y * player.speed; player.pos.z += player.dir.z * player.speed; } } Ruch do tyłu realizujemy poprzez odejmowanie zamiast dodawania Warto zwrócić uwagę, że 'w' i 'W' to dwa różne klawisze i zdarzenia dla jednego nie pokrywają się ze zdarzeniami drugiego 25.04.13 Bartosz Bazyluk 17
Ruch kamery w bok Aby uwzględnić kierunek widzenia kamery, ruszając się na boki należy poruszać się po kierunku prostopadłym W założeniu mamy poruszać się tylko po płaszczyźnie XZ e p' d p p ' = p+ speed e, e d Jak wyznaczyć wektor prostopadły do danego? d =(d x d y d z) e=( d z 0 d x ) 25.04.13 Bartosz Bazyluk 18
Obrót kamery Obrót kamery jest zmianą kierunku jej widzenia, bez wpływu na punkt zaczepienia W założeniu mamy poruszać się tylko po płaszczyźnie XZ Rezultatem jest wektor kierunku obrócony o zadany kąt Jak wyliczyć jego współrzędne? d p d' 25.04.13 Bartosz Bazyluk 19
Obrót wektora kierunku w 2D y Dla uproszczenia sprowadźmy problem do dwóch wymiarów. d Będziemy dokonywać obrotu tylko w obrębie jednej płaszczyzny, którą roboczo nazwiemy x/y. x Chcąc poruszać się po płaszczyźnie x/z układu współrzędnych OpenGL należy odpowiednio zmienić oznaczenie osi! 25.04.13 Bartosz Bazyluk 20
Obrót wektora kierunku w 2D y Chcemy dokonać obrotu kamery o zadany kąt α. d α x 25.04.13 Bartosz Bazyluk 21
Obrót wektora kierunku w 2D y d Naszym wynikiem powinien być nowy wektor kierunku d', uzyskany po obróceniu pierwotnego wektora d wokół początku układu współrzędnych. α d' x 25.04.13 Bartosz Bazyluk 22
Obrót wektora kierunku w 2D y d = (x d ; y d ) Można przyjąć, że wektory zaczepione w początku układu współrzędnych to położenia punktów znajdujących się na ich końcach. d' = (x d '; y d ') x 25.04.13 Bartosz Bazyluk 23
Obrót wektora kierunku w 2D y d = (x d ; y d ) Inny sposób opisania położenia punktu na płaszczyźnie: współrzędne biegunowe ϕ r d' = (x d '; y d ') Położenie definiuje się jako odległość od bieguna (u nas: pocz. układu współrzędnych) oraz kąt pomiędzy osią biegunową (u nas: +y), a promieniem wodzącym. d =(x d ; y d )=(r ;ϕ) współrzędne kartezjańskie współrzędne biegunowe x 25.04.13 Bartosz Bazyluk 24
Obrót wektora kierunku w 2D y d = (x d ; y d ) Aby wyliczyć współrzędne biegunowe odpowiadające kartezjańskim, wystarczy posłużyć się dwoma prostymi wzorami. ϕ r d' = (x d '; y d ') x Funkcja cyklometryczna atan2 jest dwuargumentową odmianą funkcji arcus tangens, biorącą pod uwagę ćwiartkę układu w której znajduje się zadany kąt. [http://en.wikipedia.org/wiki/atan2] d =( x d ; y d )=(r ;ϕ) r= x 2 + y 2 ϕ=atan2( y, x) 25.04.13 Bartosz Bazyluk 25
Obrót wektora kierunku w 2D y d = (x d ; y d ) Pamiętamy, że nasz wektor kierunku jest zawsze wektorem jednostkowym. W takim razie długość promienia wodzącego r będzie zawsze równa 1. ϕ r=1 d' = (x d '; y d ') x d =(x d ; y d )=(r ;ϕ)=(1 ;ϕ) r= x 2 + y 2 =1 ϕ=atan2( y, x) 25.04.13 Bartosz Bazyluk 26
Obrót wektora kierunku w 2D y d = (x d ; y d ) Zwróćmy uwagę, że we współrzędnych biegunowych podczas obracania wektora, ulegnie zmianie tylko jedna współrzędna. ϕ α d' = (x d '; y d ') x Nowy kąt zostanie powiększony o wartość kąta α o który dokonujemy obrotu. d =(x d ; y d )=(r ;ϕ)=(1 ;ϕ) r= x 2 + y 2 =1 ϕ=atan2( y, x) d '=(1 ;ϕ+α) 25.04.13 Bartosz Bazyluk 27
Obrót wektora kierunku w 2D y d = (x d ; y d ) Mając współrzędne biegunowe wektora po dokonaniu obrotu, wystarczy powrócić do kartezjańskiego układu współrzędnych. ϕ α d' = (x d '; y d ') x Służą do tego dwa proste wzory. d =(x d ; y d )=(r ;ϕ)=(1 ;ϕ) r= x 2 + y 2 =1 ϕ=atan2( y, x) d '=(1 ;ϕ+α) x=r cos(ϕ) y=r sin(ϕ) 25.04.13 Bartosz Bazyluk 28
Obrót wektora kierunku w 2D y d = (x d ; y d ) Należy pamiętać, że r=1, przez co sprawa jest jeszcze prostsza. ϕ α d' = (x d '; y d ') x d =(x d ; y d )=(r ;ϕ)=(1 ;ϕ) r= x 2 + y 2 =1 ϕ=atan2( y, x) d '=(1 ;ϕ+α)=(cos(ϕ+α);sin(ϕ+α)) x=r cos(ϕ) y=r sin(ϕ) 25.04.13 Bartosz Bazyluk 29
Oświetlenie Celem symulacji oświetlenia jest taka modyfikacja kolorów obiektów, aby sprawiały wrażenie oświetlonych fizycznymi źródłami światła Należy rozróżnić dwie rzeczy: Oświetlenie model matematyczny pozwalający na wyliczenie koloru z uwzględnieniem wpływu źródeł światła, cech materiału i środowiska Cieniowanie (ang. shading nie shadowing!) sposób dystrybucji koloru pomiędzy miejscami, dla których jest on wyliczony Źródło obrazów: http://docs.techsoft3d.com Płaskie Gouraud Phonga 25.04.13 Bartosz Bazyluk 30
Oświetlenie Najczęściej używanym modelem oświetlenia w grafice czasu rzeczywistego jest model Phonga Niezależnie od tego, czy stosujemy cieniowanie płaskie, Gouraud, czy Phonga model oświetlenia jest czymś osobnym Model Phonga opiera się na rozbiciu światła na trzy komponenty: I A =(i r A i g A i b A) I I D =(ir D i g D i bd) S =(ir S i gs i bs) Źródło obrazów: http://wikipedia.org I=I A + I D + I S 25.04.13 Bartosz Bazyluk 31
Światło i materiały Komponenty Ambient, Diffuse oraz Specular określamy zarówno dla każdego ze źródeł światła, jak i dla materiału z którego zbudowana jest powierzchnia obiektu L L A =(l r A l g A l ba) L D =(l r D l g D l bd) S =(l r S l g S l bs) M M A =(m r A m g A m ba) M D =(mr D m g D m bd) S =(mrs m g S m bs) 25.04.13 Bartosz Bazyluk 32
Światło i materiały Komponenty Ambient, Diffuse oraz Specular określamy zarówno dla każdego ze źródeł światła, jak i dla materiału z którego zbudowana jest powierzchnia obiektu L L A =(l r A l g A l ba) L D =(l r D l g D l bd) S =(l r S l g S l bs) M M A =(m r A m g A m ba) M D =(mr D m g D m bd) S =(mrs m g S m bs) 25.04.13 Bartosz Bazyluk 33
Wektory normalne W celu określenia skierowania punktu na powierzchni bryły, definiuje się tzw. wektory normalne Są to wektory, które są prostopadłe do powierzchni Zazwyczaj są to wektory jednostkowe 25.04.13 Bartosz Bazyluk 34
Wektory normalne W celu określenia skierowania punktu na powierzchni bryły, definiuje się tzw. wektory normalne Są to wektory, które są prostopadłe do powierzchni Zazwyczaj są to wektory jednostkowe Wektory normalne określa się dla wierzchołków 25.04.13 Bartosz Bazyluk 35
Wektory normalne W celu określenia skierowania punktu na powierzchni bryły, definiuje się tzw. wektory normalne Są to wektory, które są prostopadłe do powierzchni Zazwyczaj są to wektory jednostkowe Wektory normalne określa się dla wierzchołków Na ich podstawie można określić wpływ światła padającego w zadanym kierunku na kolor powierzchni obiektu 25.04.13 Bartosz Bazyluk 36
Wektory normalne W celu uzyskania łagodnych krawędzi, wektory normalne powinny być identyczne dla wszystkich ścian zbiegających się w danym wierzchołku Przykład (rzut boczny): W celu przybliżenia gładkiej niebieskiej powierzchni z użyciem czerwonych ścian, należy stosować identyczne wektory normalne w wierzchołkach Źródło obrazów: http://cs.berkeley.edu 25.04.13 Bartosz Bazyluk 37
Wektory normalne W OpenGL wektory normalne definiujemy w sposób podobny do pozycji wierzchołków glnormal3f(0.0f, 1.0f, 0.0f); Ostatnio określony wektor normalny zostaje powiązany z każdym nowo tworzonym wierzchołkiem glnormal3f(0.0f, 1.0f, 0.0f); glvertex3f(0.0f, 0.0f, 0.0f); // normal = (0;1;0) glnormal3f(0.0f, 0.0f, 1.0f); glvertex3f(1.0f, 0.0f, 0.0f); // normal = (0;0;1) glvertex3f(0.0f, 1.0f, 0.0f); // normal = (0;0;1) glvertex3f(0.0f, 0.0f, 1.0f); // normal = (0;0;1) 25.04.13 Bartosz Bazyluk 38
Model oświetlenia Phonga W tym wypadku iloczyn dwóch wektorów rozumiemy jako iloczyn ich komponentów! Kolor w danym wierzchołku/fragmencie (zależnie, czy cieniowanie Gouraud, czy Phonga), oblicza się sumując wartości dla wszystkich źródeł światła: lights I = i=1 Dla każdego źródła światła: I i A + I i D +I i S Komponent Ambient to prosty iloczyn wartości RGB światła i wartości RGB materiału I A =L A M A Komponent Diffuse dodatkowo zależy od współczynnika lambertowskiego, zależnego od kąta pomiędzy wektorem normalnym N a wektorem kierunku padania światła L I D =k D L D M D, k D = L N Komponent Specular zależy od spójności wektora odbitego R oraz wektora kierunku obserwacji V; wielkość rozbłysku zależy od parametru shininess α I S =k S L S M S, k S =( R V ) α, R=2( L N ) N L 25.04.13 Bartosz Bazyluk 39
Światło w OpenGL Model oświetlenia Phonga jest częścią nieprogramowalnego potoku renderowania w OpenGL Włączenie obliczania oświetlenia: glenable(gl_lighting); Nieprogramowalny potok renderowania OpenGL oferuje dwa rodzaje cieniowania Cieniowanie płaskie glshademodel(gl_flat); Cieniowanie Gouraud (domyślne; per-vertex) glshademodel(gl_smooth); Nie ma możliwości włączenia cieniowania Phonga (per-fragment) nie pisząc własnych programów cieniujących 25.04.13 Bartosz Bazyluk 40
Światło w OpenGL W nieprogramowalnym potoku renderowania OpenGL (ang. fixed pipeline) mamy możliwość zdefiniowania przynajmniej ośmiu źródeł światła (zależy od implementacji) Do źródeł światła odwołujemy się z użyciem stałych: GL_LIGHT0, GL_LIGHT1, GL_LIGHT2,... Można też użyć sposobu: GL_LIGHT0 + 23 Aby dane źródło światła działało, musi zostać włączone: glenable(gl_light1); Domyślnie tylko GL_LIGHT0 jest włączone Poszczególne komponenty źródła źwiatła definiujemy następująco: float l1amb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; float l1dif[4] = { 1.0f, 0.0f, 1.0f, 1.0f }; float l1spe[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; gllightfv(gl_light1, GL_AMBIENT, l1amb); gllightfv(gl_light1, GL_DIFFUSE, l1dif); gllightfv(gl_light1, GL_SPECULAR, l1spe); 25.04.13 Bartosz Bazyluk 41
Materiały Materiał jest cechą danego wierzchołka (analogicznie do koloru ustawianego za pomocą glcolor*()) Po ustawienia materiału i włączeniu oświetlenia, kolory wierzchołków domyślnie nie są już brane pod uwagę Oddzielnie ustawia się materiał strony przedniej i tylnej, w praktyce zazwyczaj używa się jedynie przedniej (GL_FRONT) Materiał ustawia się następująco: float mamb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; float mdif[4] = { 1.0f, 1.0f, 0.0f, 1.0f }; float mspe[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; glmaterialfv(gl_front, GL_AMBIENT, mamb); glmaterialfv(gl_front, GL_DIFFUSE, mdif); glmaterialfv(gl_front, GL_SPECULAR, mspe); Dodatkowo można określić wartość eksponenty związanej z komponentem specular: glmaterialf(gl_front, GL_SHININESS, 20.0f); Im większe shininess, tym mniejszy rozbłysk 25.04.13 Bartosz Bazyluk 42
Źródła światła Możemy wyróżnić trzy podstawowe rodzaje źródeł światła: Światła kierunkowe Wszystkie promienie są do siebie równoległe Konieczne jest zdefiniowanie kierunku świecenia W OpenGL: float lightpos[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; gllightfv(gl_light0, GL_POSITION, lightpos); Ważne! Ostatni komponent pozycji musi być równy 0, wtedy źródło światła traktowane jest jako kierunkowe. Inaczej jest ono światłem pozycyjnym. Ważne! Podajemy wartości będące przeciwieństwem kierunku świecenia. W przykładzie promienie będą się rozchodzić w kierunku (0;0;-1). 25.04.13 Bartosz Bazyluk 43
Źródła światła Możemy wyróżnić trzy podstawowe rodzaje źródeł światła: Światła punktowe Światło pozycyjne, promienie rozchodzą się dookólnie Konieczne jest określenie pozycji źródła światła W OpenGL: float lightpos[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; gllightfv(gl_light0, GL_POSITION, lightpos); Ważne! Ostatni komponent pozycji musi być równy 1, wtedy źródło światła traktowane jest jako pozycyjne. Inaczej jest ono światłem kierunkowym. 25.04.13 Bartosz Bazyluk 44
Źródła światła Możemy wyróżnić trzy podstawowe rodzaje źródeł światła: Światła punktowe (c.d.) Dodatkowo można określić tłumienie wraz z odległością Składa się z trzech komponentów: stałego, liniowego i kwadratowego W OpenGL: 1 a(d )= k c +k l d +k q d 2 gllightf(gl_light0, GL_CONSTANT_ATTENUATION, 2.0f); gllightf(gl_light0, GL_LINEAR_ATTENUATION, 1.0f); gllightf(gl_light0, GL_QUADRATIC_ATTENUATION, 0.5f); Tłumienie wpływa na wszystkie komponenty światła 25.04.13 Bartosz Bazyluk 45
Źródła światła Możemy wyróżnić trzy podstawowe rodzaje źródeł światła (c.d.): Światła stożkowe (ang. spotlights) Skierowane światło punktowe o ograniczonym kącie Oprócz pozycji, określa się także kierunek światła oraz szerokość kąta świecenia: float lightdir[3] = { 0.0f, 0.0f, 1.0f }; gllightfv(gl_light0, GL_SPOT_DIRECTION, lightdir); gllightf(gl_light0, GL_SPOT_CUTOFF, 45.0f); 25.04.13 Bartosz Bazyluk 46
Źródła światła Zadania: Stworzyć punktowe źródło światła zawieszone nad sceną Stworzyć dwa punktowe źródła światła z tłumieniem, które można niezależnie poruszać za pomocą klawiszy 25.04.13 Bartosz Bazyluk 47
Bartosz Bazyluk GRAFIKA CZASU RZECZYWISTEGO Interakcja, ruch kamery, oświetlenie. Grafika komputerowa i wizualizacja, Bioinformatyka S1, II Rok