20:00 | Temat wykladu: | |
C++: Wstęp do Raytracingu (by Gynvael, 17.08.06 ) @ wyklady.net | ||
Komentarze: http://forum.wyklady.net/index.php?topic=77.0 | ||
20:09 | Gynvael | Witam wszystkich na wykladzie o zacnym tytule 'Wstep do Raytracingu' ;> |
Gynvael | W sumie kto ja jestem mowilem juz na innych wykladach | |
Gynvael | Wiec w ramach wstepu powiem tylko ze wyklad dedykuje mojej ukochanej Arashi i Salvationowi ;> | |
20:10 | Gynvael | (ktory siedzi kolo mnie i sie usmiecha ;>) |
Gynvael | (glupio usmiecha) | |
Gynvael | Dobra | |
Gynvael | Co bedzie potrzebne: | |
Gynvael | 1) komputer + kompilator jezyka c++ (najlepiej z rodziny GCC) | |
20:11 | Gynvael | pod windowsem bedzie to MinGW GCC (mingw.org), pod linuxem standardowe linuxowe gcc ;> |
Gynvael | 2) jako ze bedzie pare animacji, przyda sie player (polecam mplayer) ktory umie odtworzyc animacje .avi (DivX 4.X) | |
Gynvael | http://divx.pl/download.php?id=001001 - divx | |
20:13 | Gynvael | yh.. sponsorem wykladu jest Quit (Ping timeout) by TPSA |
Gynvael | http://divx.pl/download.php?id=001001 - divx | |
Gynvael | tam mozna sciagnac odpowiedni kodek ;> | |
Gynvael | 3) troche wolnego czasu zeby to czytac ;> | |
Gynvael | Wszelkie pytania podczas wykladu prosze na priv ;> | |
Gynvael | OK ;> | |
Gynvael | Na dzisiejszym wykladzie (pierwszym z kilku) bedzie o: | |
Gynvael | - co to jest Raytracing | |
20:14 | Gynvael | - na czym on polega |
Gynvael | - jak napisac prosty engine raytracingowy w C++ | |
Gynvael | oraz omowienie paru rzeczy takich jak oswietlenie, prymitywy, etc | |
Gynvael | ;> | |
Gynvael | Dobra.. jedziemy ;> | |
20:15 | Gynvael | Raytracing jest to jedna z metod renderingu scen 3D |
20:16 | Gynvael | Wymaga wiekszej ilosci obliczen niz niektore inne metody (np te znane z gier typu Doom 1 ;>) |
Gynvael | ale daje duzo ladniejsze rezultaty. | |
Gynvael | Dodam ze nie bedziemy mowic o Raytracingu w czasie rzeczywistym, a o raytracingu scen do bitmap po prostu ;> | |
20:17 | Gynvael | <dragon> moze stworzyc kanal #wyklady-dyskusje ? moznaby gadac w trakcie wykladu ;> |
Gynvael | Na czym polega ta metoda ? | |
Gynvael | Popatrzy na chwile na monitor | |
Gynvael | To co widzimy to zbior pixeli | |
Gynvael | jesli narysujemy np kolo, to na dobra sprawe bedzie ono tylko zbiorem pixeli | |
20:18 | Gynvael | |
Gynvael | kazdy pixel ma swoj kolor (RGB) | |
20:19 | Gynvael | czemu mowie o pixelach ? poniewaz raytracing jest metoda renderingu w ktora stara sie odpowiedziec na pytanie |
Gynvael | jakiego koloru jest dany pixel | |
Gynvael | wyobrazmy sobie nasz ekran w przestrzeni 3D | |
Gynvael | jest to nie trudne raczej ;> | |
Gynvael | umiesmy przed ekranem miotacz promieni ("kamere") | |
20:20 | Gynvael | i wystrzelmy promien przez nasz ekran, przez kazdy konkretny pixel po kolei |
Gynvael | zalozmy teraz ze za naszym ekranem sa jakies obiekty | |
Gynvael | np jest jedna kula | |
Gynvael | ||
Gynvael | ekran jako prostokat | |
20:21 | Gynvael | np prostokat o wymiarach (w pixelach) 640x480 |
Gynvael | na powyzszym obrazku jest rzut z boku (2D) na to o czym mowie | |
Gynvael | po lewej jest kamera, jest ekran widziany z boku | |
Gynvael | i jest kula (obiekt, prymityw) | |
20:22 | Gynvael | Zauwazyc mozna nastepujace rzeczy |
Gynvael | mianowicie: czesc promieni przelatuje gdzies obok kuli | |
Gynvael | ale czesc w nia trafia | |
Gynvael | jesli promien w nic nie trafi | |
Gynvael | mozemy uznac ze trafil w tlo, czyli np czarny kolor | |
Gynvael | a jesli w cos trafi, np w kule, to mozemy w danym pixelu (przez ktory promien przechodzi) | |
20:23 | Gynvael | umiescic np kolor kuli ;> |
Gynvael | zyli czerwony | |
Gynvael | *czyli | |
Gynvael | widac tutaj zasadnicza roznice od tradycyjnej rasteryzacji 2D->3D uzytej np w w/w doom 1 | |
20:24 | Gynvael | w tradycyjnej metodzie chodzi o odpowiedz na pytanie, "gdzie na ekranie lezy dany obiekt" |
20:25 | Gynvael | natomiast w raytracingu chodzi bardziej o odpowiedz "czy patrzac przez ten jeden dany pixel widzimy jakis obiekt" |
Gynvael | ok ;> tyle wstepu | |
Gynvael | jak wyglada przykladowa scena wyrenderowana przez raytracer ? np tak: | |
20:26 | Gynvael | |
Gynvael | ||
Gynvael | ||
20:27 | Gynvael | wyglada to ladnie.. ale jak cos takiego napisac |
Gynvael | i czemu akurat w C++ | |
Gynvael | ;> | |
Gynvael | w raytracingu operujemy sporo na wektorach, a C++ posiada sliczne przeciazenia operatorow, dzieki czemu niektore kawalki kodu zajmujace po 10 linii w C, beda zajmowac jedna linijke w C++ | |
20:28 | Gynvael | co to jest wektor ? |
Gynvael | w duzym skrocie, jest to 'odcinek' (pamietacie z matematyki co to ne?) zaczynajacy sie w punkcie 0,0 i konczacy gdziekolwiek (np 1, 2) | |
20:29 | Gynvael | (jesli mowimy o 2D) |
Gynvael | http://pl.wikipedia.org/wiki/Wektor | |
20:30 | Gynvael | wektor ma swoj kierunek (tj wskazuje pewien kierunek) |
Gynvael | wektor mozna okreslic na dwa sposoby | |
Gynvael | 1) podajac jego koniec, np w przypadku 3D moze to byc [1,2,3] | |
Gynvael | 2) podajac kat od osi postrzegolnych i dlugosc wektora (ale to nam nie potrzebne) | |
Gynvael | na wektorach mozna wykonywac rozne operacje matematyczne | |
20:31 | Gynvael | np wektory mozna dodawac |
Gynvael | ||
20:32 | Gynvael | mozna odracac je, mnozyc na dwa sposoby (iloczyn skalarny i wektorow) |
Gynvael | kazdy wektor ma takze dlugosc | |
Gynvael | dlugosc liczy sie bardzo standardowym wzorem: sqrt( X*X + Y*Y + Z*Z ) w przypadku 3D, gdzie X,Y,Z to koordynaty wektora | |
Gynvael | mozna je rowniez normalizowac | |
20:33 | Gynvael | co to znaczy 'znormalizowac wektor' ? |
Gynvael | chodzi tutaj o zmniejszenie dlugosci wektora do jednosci (1), przy jednoczesnym zachowaniu kierunku jaki wskazuje | |
Gynvael | uzyskuje to sie dzielac kazda wspolrzedna wektora (X Y Z) przez jego dlugosc ;> | |
Gynvael | etc | |
20:34 | Gynvael | Pierwsza rzecza ktora jest potrzebna przy silniku raytracingowym jest jakas polsensowna |
Gynvael | implementacja wektorow wlasnie ;> | |
Gynvael | http://wyklady.net/ray1/r1.zip | |
20:35 | Gynvael | Pod tym linkiem znajduja sie zrodla implementacji razem z prostym programem testujacym |
Gynvael | rzucmy okiem na plik Vector3D.h | |
Gynvael | czyli interfejs wektorow 3D | |
Gynvael | Vector3D(); | |
Gynvael | Vector3D(double x, double y, double z); | |
Gynvael | tworzac wektor mozna (ale nie trzeba) podac mu odrazu wspolzedne | |
20:36 | Gynvael | void PrintVector(); |
Gynvael | jest tam tez funkcja do wypisywania na konsoli pozycji wektora | |
Gynvael | Vector3D& operator= (Vector3D v); | |
Gynvael | Vector3D& operator-= (Vector3D v); | |
Gynvael | Vector3D& operator+= (Vector3D v); | |
Gynvael | Vector3D operator+ (Vector3D v); | |
Gynvael | Vector3D operator- (Vector3D v); | |
Gynvael | Vector3D Cross(Vector3D v); | |
Gynvael | double Dot(Vector3D v); | |
Gynvael | double Length(); | |
Gynvael | double SqrLength(); | |
Gynvael | void Norm(); | |
Gynvael | zestaw operacji na wektorach | |
20:37 | Gynvael | odejmowanie, dodawanie, pobranie dlugosci, pobranie kwadratu dlugosci, normalizacja, dot i cross (czyli iloczyn skalarny i wektorowy) |
Gynvael | Vector3D operator* (double r); | |
Gynvael | Vector3D& operator*= (double r); | |
Gynvael | jest tez operator ktory umozliwia pomnozenie wektora przez skalar (liczbe) | |
Gynvael | np majac wektor | |
Gynvael | [1,2,3] | |
Gynvael | chcemy go pomnozyc przez 5 | |
Gynvael | [1,2,3] * 5 == [5, 10, 15] ;> | |
20:38 | Gynvael | Vector3D operator* (Vector3D v); |
Gynvael | Vector3D operator*= (Vector3D v); | |
Gynvael | to sa dwie dodatkowe funkcje | |
Gynvael | z wektorami nei majace w tym wypadku nic wspolnego | |
Gynvael | o nich za chwile | |
Gynvael | skompilujmy caly kod ktory tam jest | |
Gynvael | (jak skompilowac ? jest w pliku vtest.c na gorze) | |
20:39 | Gynvael | 20:39:13 >g++ Vector3D.cpp vtest.cpp -Wall -o vtest.exe |
Gynvael | 20:39:32 >vtest.exe | |
Gynvael | [1.000000, 4.000000, 9.000000] | |
Gynvael | 20:39:35 > | |
20:40 | Gynvael | w vtest.c jest dosc prosty program |
Gynvael | Vector3D a(1.0,2.0,3.0), b; | |
Gynvael | b = a * a; | |
Gynvael | b.PrintVector(); putchar('\n'); | |
Gynvael | OK ;> | |
Gynvael | jak dzialaja wektory dokladnie ? pozostawiam to wlasnej analizie ;> | |
Gynvael | (jako ze nie jest to tematem tego wykladu) | |
20:41 | Gynvael | teraz taka mala dygresja |
Gynvael | jak wspomnialem kazdy pixel ma kolor | |
Gynvael | zazwyczaj poslugujemy sie kolorami 24-bitowymi (lub 32) | |
Gynvael | kolor sklada sie z 3ch skladowych | |
Gynvael | R - Red - Czerwona | |
Gynvael | G - Green - Zielona | |
Gynvael | B - Blue - Niebieska | |
Gynvael | na kazda skladowa przypada 8 bitow | |
20:42 | Gynvael | czyli przyjmuje ona wartosci od 0 do 255 (0 - 2^8-1) |
Gynvael | wygodne jesli chodzi o ekran | |
Gynvael | ale troche nie wygodne jesli chodzi o sama grafike | |
Gynvael | a co jesli 'rozdzielczosc kolorow' 256 nam nie wystarcza ? | |
Gynvael | np 1024 odcienie byly by lepsze etc | |
20:43 | Gynvael | wieksza 'rozdzielczosc' w czasie obliczen zapewnie lepsze efekty w koncowym obrazie |
Gynvael | (mimo iz sam koncowy obraz ma te 256 odcieni na barwe) | |
20:44 | Gynvael | w raytracingu bede korzystal z reprezentacji koloro nie (unsigned int) 0-255, a (double) 0.0 - 1.0 |
Gynvael | wszystkie wartosci ponizej 0.0 bede traktowal jak 0.0 przy konwersji 0.0-1.0 -> 0-255 | |
Gynvael | a wszystkie powyzej jako 1.0 | |
20:45 | Gynvael | ok.. co mozna robic z kolorami ? |
Gynvael | dodawac je | |
Gynvael | odejmowac | |
Gynvael | mnozyc | |
Gynvael | etc etc | |
Gynvael | wiec przydalo by sie napisac klase ktora to obsluguje | |
Gynvael | ale w8 w8.. Vector3D bardzo podobna funkcjonalnosc zapewnia | |
Gynvael | brakuje tam tylko mnozenia kolorow | |
Gynvael | tj mnozenia kolorOUT = kolorA.R * kolorB.R etc | |
20:46 | Gynvael | kazdej barwy oddzielnie |
Gynvael | Vector3D operator* (Vector3D v); | |
Gynvael | Vector3D operator*= (Vector3D v); | |
Gynvael | te dwa operatory wlasnie do tego sluza | |
Gynvael | nie sa to operacje na wektorach, ale na kolorach | |
Gynvael | jako ze przyjmu Vector3D operator* (Vector3D v); | |
Gynvael | Vector3D operator*= (Vector3D v); | |
Gynvael | eh sorx | |
Gynvael | jako ze uzywal bede Vector3D do kolorow, to oprocz oznaczen x,y,z | |
Gynvael | struct { double x, y, z; }; | |
Gynvael | struct { double r, g, b; }; | |
Gynvael | pojawily tam tez sie oznaczenia r, g, b ;> | |
20:47 | Gynvael | dobra |
Gynvael | mamy wiec cos do wektorow i cos do kolorow | |
Gynvael | teraz by sie przydalo cos do trzymania efektu / koloru pixeli | |
Gynvael | czyli obsluga bitmap | |
Gynvael | najlepiej z mozliwoscia zrzucania do pliku | |
Gynvael | http://wyklady.net/ray1/r2.zip | |
20:48 | Gynvael | Jest tam mala prosta klasa Image24, ktora dostarcza bardzo podstawowa obluge bitmap 24-bitowych |
20:49 | Gynvael | oraz umozliwia zapisanie danej bitmapy jako TGA (czemu TGA ? bo to bardzo prosty format ;>) |
Gynvael | przy tworzeniu nowego obiektu Image24 podaje sie szerokosc i wysokosc bitmapy | |
Gynvael | a potem po prostu modyfikuje sie wartosci w tablicy 'data' | |
20:50 | Gynvael | tablica data to tablice unsigned intow |
Gynvael | kazdy pixel to jeden element w tej tablicy (ofc) | |
Gynvael | kolor pixeli sa tam zapisane tak: 0xRRGGBB ;> | |
Gynvael | void SaveAsTGA(const char *filename) const; | |
Gynvael | co robi ta funkcja latwo sie domyslic | |
Gynvael | po skompiowaniu i uruchomieniu testowego programu (patrz plik itest.cpp) | |
20:51 | Gynvael | tworzy sie plik dump.tga ktory zawiera 'oczy zlego kaczora donalda', czy tam jakas funkcje trygonometryczna, jak kto woli ;> |
Gynvael | ||
20:52 | Gynvael | dobra.. mamy 3 rzeczy teraz |
Gynvael | 1) wektory | |
Gynvael | 2) kolory | |
Gynvael | 3) bitmapu | |
Gynvael | wiec mozemy sie brac za sam silnik | |
20:53 | Gynvael | co powinien zawierac silnik na tym etapie ? |
Gynvael | 1) liste prymitywow (obiektow) | |
Gynvael | 2) umozliwiac dodawanie obiektow do sceny | |
20:54 | Gynvael | 3) umozliwiac wyrenderowanie tej sceny |
Gynvael | dodatkowo | |
Gynvael | kazdy rodzaj obiektu powinien zawierac funkcje | |
Gynvael | ktora otrzyma promien (promien dla nas to bedzie punkt w przestrzeni skad zostal wystrzelony promien (kamera) oraz wektor kierunkowy (tj wektor pokazujacy kierunkek w ktorym leci promien)) | |
20:55 | Gynvael | i powie czy promien TRAFIA w dany obiekt (tj sie z nim przecina |
20:56 | Gynvael | jak powinien dzialac rendering |
Gynvael | Dla kazdego pixela powinien byc wystrzelony promien z kamery | |
20:57 | Gynvael | Po czym z listy obiektow musi zostac wybrany obiekt ktory lezy najblizej punktu wystrzelenia promienia (kamery) i sie przecina z promieniem |
Gynvael | I w naszym przypadku teraz | |
Gynvael | powinien zostac zwrocony kolor czarny jesli promien nie trafil w zaden obiekt | |
20:58 | Gynvael | lub bialy jesli trafil w jakis |
Gynvael | http://wyklady.net/ray1/r3.zip | |
Gynvael | rzucmy okiem na ten projekt | |
20:59 | Gynvael | widac tam pliki z r2 i r1 czyli Image24.* i Vector3D.* |
Gynvael | oraz sporo nowych | |
Gynvael | krotko o nich: | |
Gynvael | Primitive.* to deklaracja interfejsu prymitywow.. patrzac w Primitive.h dowiemy sie jakie sa wspolne cechy kazdego obiektu | |
21:00 | Gynvael | poki co jedyna wspolna cecha obiektow jest to ze maja funkcje |
Gynvael | int Intersect(Ray &ray, double &dist) | |
Gynvael | Intersect (eng. przeciecie) | |
Gynvael | ktora mowi czy promien przecina sie z obiektem | |
21:01 | Gynvael | < SBKch> jak skompilowac to r3.zip? |
Gynvael | napisac 'make' | |
Gynvael | lub w skrajnych przypadkach mingw32-make | |
Gynvael | dodam ze 'make test' uruchomi aplikacje | |
Gynvael | ok | |
Gynvael | PrimSphere.* | |
21:02 | Gynvael | to jest pierwszy i jedyny poki co rodzaj obiektu supportowany przez nasz engine |
Gynvael | Sphere = sfera (pusta kula) | |
Gynvael | czemu kula ? | |
Gynvael | poniewaz matematycznie bardzo latwo jest sprawdzac przeciecie promienia z kula | |
21:03 | Gynvael | kula jest opisana przez pozycje w wirtualnym swiecie 3D naszej sceny (X,Y,Z) oraz przez promien |
Gynvael | przeciecie sprawdza sie po prostu poprzez sprawdznie czy prosta bedaca promieniem nie przechodzi dalej niz promien_kuli od punktu srodek_kuli ;> | |
21:04 | Gynvael | ... |
Gynvael | <@Gynvael> przeciecie sprawdza sie po prostu poprzez sprawdznie czy prosta bedaca promieniem nie przechodzi dalej niz promien_kuli od punktu srodek_kuli ;> | |
Gynvael | <@Gynvael> (patrz odleglosc punktu od prostej) | |
21:05 | Gynvael | Ray.* |
Gynvael | tam mamy malutka klase opisujaca promien | |
Gynvael | jak mowilem juz wczesniej promien to dla nas punkt wystrzelenia promienia i kierunkek w ktorym leci ;> | |
Gynvael | public: | |
Gynvael | Vector3D Origin, Direction; | |
Gynvael | Origin == zrodlo | |
Gynvael | Direction == kierunek | |
Gynvael | RTEngine.* | |
21:06 | Gynvael | to jest nasz Silnik |
Gynvael | poki co we wnetrzu silnika sa dwie rzeczy | |
Gynvael | std::vector<Primitive*> PrimitiveList; | |
Gynvael | Vector3D Trace(Ray ray); | |
21:07 | Gynvael | pierwsze to lista obiektow o ktorej mowilem wczesniej |
Gynvael | a drugie to funkcja sledzaca jeden promien.. funkcja ta wywolywana jest dla KAZDEGO PIXELA ekranu i zwraca KOLOR pixela w tym miejscu | |
Gynvael | poki co kolor to czarny - nie trafiono w obiekt | |
Gynvael | bialy - trafiono | |
Gynvael | a co do interfejsu | |
Gynvael | void AddSphere(Vector3D Center, double Radius); | |
Gynvael | void Render(Image24 *img); | |
21:08 | Gynvael | to dwie funkcje sa |
Gynvael | AddSphere dodaje kule do sceny | |
Gynvael | a Render renderuje scene do bitmapy | |
Gynvael | przed odpaleniem zajrzyjmy jeszcze do test.cpp | |
Gynvael | to jest prosty programik testowy | |
21:09 | Gynvael | Image24 Img(640, 480); |
Gynvael | RTEngine RT; | |
Gynvael | RT.AddSphere(Vector3D(0.0,0.0,3.0), 1.0); | |
Gynvael | RT.Render(&Img); | |
Gynvael | Img.SaveAsTGA("dump.tga"); | |
Gynvael | kod jest bardzo krotki | |
Gynvael | 1) tworzona jest bitmapa 640x480 | |
Gynvael | 2) tworzony obiekt-engine | |
Gynvael | 3) dodawana jest jedna kula do sceny, na miejsce [0,0,3] o promieniu 1 | |
Gynvael | 4) scena jest renderowana do bitmapy | |
21:10 | Gynvael | 5) i zapisywana w pliku dump.tga |
Gynvael | dodam w tym miejscu ze kamere 'na sztywno' poki co umiescilem w miejscu 0,0,-5 | |
Gynvael | natomiast ekran lezy dla nad na plaszczyznie XY (Z = 0), w koordynatach (przypominam ze ekran to prostokat): | |
21:12 | Gynvael | co ostatnie doszlo ? |
DJ_cool_ | 21:10 <@Gynvael> natomiast ekran lezy dla nad na plaszczyznie XY (Z = 0), w koordynatach (przypominam ze ekran to prostokat): | |
21:13 | Gynvael | lewy gorny rog: -2, 1.5, 0 |
Gynvael | prawy dolny: 2, -1.5, 0 | |
Gynvael | thx DJ_cool_;> | |
DJ_cool_ | tak | |
DJ_cool_ | aw. | |
Gynvael | prosze tez jeszcze zauwazyc ze kula na scenie lezy bezposrednio przed kamera | |
Gynvael | tj mozna sie jej na srodku ekranu spodziewac | |
Gynvael | ok ,kompilujemy i odpalamy | |
21:14 | Gynvael | 21:14:05 >make test |
Gynvael | g++ -c -o Image24.o Image24.cpp | |
Gynvael | g++ -c -o PrimSphere.o PrimSphere.cpp | |
Gynvael | g++ -c -o Ray.o Ray.cpp | |
Gynvael | g++ -c -o RTEngine.o RTEngine.cpp | |
Gynvael | g++ -c -o test.o test.cpp | |
Gynvael | g++ -c -o Vector3D.o Vector3D.cpp | |
Gynvael | g++ -c -o Primitive.o Primitive.cpp | |
Gynvael | g++ Image24.o PrimSphere.o Ray.o RTEngine.o test.o Vector3D.o Primitive.o -o rtt | |
Gynvael | est.exe | |
Gynvael | rttest.exe | |
Gynvael | 21:14:19 > | |
Gynvael | utworzyl sie plik dump.tga | |
Gynvael | ||
Gynvael | tak wyglada jego zawartosc | |
21:15 | Gynvael | jest kula na srodku, zgadza sie |
Gynvael | przyjrzyjmy sie blizej procedurze Render w silniku | |
Gynvael | Vector3D CameraPos(0.0, 0.0, -5.0); | |
Gynvael | jak nietrudno sie domyslic, jest to pozycja kamery ;> | |
Gynvael | double WX1 = -2.0, WX2 = 2.0, WY1 = 1.5, WY2 = -1.5; | |
21:16 | Gynvael | to natomiast sa koordynaty ekranu w wirtualnej przestrzeni naszej sceny (przed chwila o nich mowilem) |
Gynvael | double DX = (WX2 - WX1) / (double)img->width; | |
Gynvael | double DY = (WY2 - WY1) / (double)img->height; | |
Gynvael | double SX = WX1, SY = WY1; | |
21:17 | Gynvael | prostokat ekranu jest podzielony na 640x480 pixeli |
Gynvael | DX to szerokosc pixela w wirtualnym swiecie (szerokosc ekranu / ilosc pixeli) | |
Gynvael | DY to wysokosc pixela | |
21:24 | Gynvael | yh |
Gynvael | na czym skonczylem? | |
21:25 | Gynvael | DX to szerokosc pixela w wirtualnym swiecie (szerokosc ekranu / ilosc pixeli) |
Gynvael | DY to wysokosc pixela | |
Gynvael | DX i DY sluza nam do wyznaczenia koordynatow kazdego pixela na ekranie | |
Gynvael | zmienne SX i SY to natomiast iteratory | |
Gynvael | sa one uzywane w petli | |
Gynvael | do wytrzelenia (Trace) promienia przez kazdy pixel | |
21:26 | Gynvael | int x, y; |
Gynvael | unsigned int *px = img->data; | |
Gynvael | x,y - kolejne iteratory | |
Gynvael | *px natomiast wskazuje w bitmapie na pixel ktory aktualnie analizujemy | |
Gynvael | for(y = 0; y < img->height; y++) | |
Gynvael | { | |
Gynvael | SX = WX1; | |
Gynvael | for(x = 0; x < img->width; x++) | |
Gynvael | { | |
Gynvael | [...] | |
Gynvael | SX += DX; | |
Gynvael | px++; | |
Gynvael | } | |
Gynvael | SY += DY; | |
Gynvael | } | |
21:27 | Gynvael | nastepnie jest petla, ktora w skrocie mozna okreslic jako 'Dla kazdego pixela z bitmapy...' |
Gynvael | co sie dzieje w tej petli ? | |
Gynvael | Ray ray(CameraPos, Vector3D(SX, SY, 0.0)); | |
Gynvael | ray.Direction = ray.Direction - ray.Origin; | |
Gynvael | ray.Direction.Norm(); | |
Gynvael | po pierwsze tworzony jest nowy promien, wyznaczany jego wektor kierunkowy | |
Gynvael | (ten wektor jest tez normalizowany) | |
Gynvael | Vector3D color = this->Trace(ray); | |
21:28 | Gynvael | po czym jest wystrzelany |
Gynvael | funkcja Trace zwraca kolor | |
Gynvael | int r, g, b; | |
Gynvael | r = (int)(color.r * 256.0); | |
Gynvael | g = (int)(color.g * 256.0); | |
Gynvael | b = (int)(color.b * 256.0); | |
Gynvael | r = r > 255 ? 255 : r < 0 ? 0 : r; | |
Gynvael | g = g > 255 ? 255 : g < 0 ? 0 : g; | |
Gynvael | b = b > 255 ? 255 : b < 0 ? 0 : b; | |
Gynvael | nastepnie ten kolor jest tlumaczony z 0.0 - 1.0 -> 0 - 255 | |
Gynvael | *px = (r << 16) | (g << 8) | (b); | |
21:29 | Gynvael | po czym jest zapisywany do bitmapy |
21:30 | Gynvael | dzialanie petli glownej jest proste |
Gynvael | rzucmy okiem teraz na Trace | |
Gynvael | Vector3D color(0.0, 0.0, 0.0); | |
Gynvael | to jest zmienna do ktorej zapisujemy obliczony kolor | |
21:31 | Gynvael | jest ona na koncu zwracana |
Gynvael | double dist = 1000000000.0f; | |
Gynvael | Primitive *prim = NULL; | |
Gynvael | te dwie zmienne pomoga nam znalezc obiekt z ktorym promien sie przecina (jesli sie przecina) i ktory jest najblizej zrodla promienia (kamery) | |
Gynvael | nastepnie jest petla | |
Gynvael | vector<Primitive*>::iterator iter; | |
Gynvael | for(iter = this->PrimitiveList.begin(); iter != this->PrimitiveList.end(); iter++) | |
Gynvael | { | |
Gynvael | double temp_dist; | |
Gynvael | if((*iter)->Intersect(ray, temp_dist) == 0) | |
Gynvael | continue; | |
Gynvael | if(temp_dist < dist) | |
Gynvael | { | |
Gynvael | prim = *iter; | |
Gynvael | dist = temp_dist; | |
Gynvael | } | |
21:32 | Gynvael | } |
Gynvael | petla dziala w ten sposob: | |
Gynvael | Dla kazdego obiektu na scenie: | |
Gynvael | Sprawdz czy promien przecina sie z obiekitem. Jesli nie, nie rob nic. | |
21:33 | Gynvael | Jesli tak, sprawdz czy obiekt jest blizej kamery niz ten co poprzednio znalezlismy. |
Gynvael | Jesli jest, to zapamietaj ten obiekt i jego odleglosc od kamery. | |
Gynvael | kamery == zrodla promienia | |
21:34 | Gynvael | jak widac uzywamy tutaj funkcji Intersect dla kazdego obiektu |
Gynvael | funkcja ta zwraca 0 jesli obiekt sie nie przecina z promieniem | |
Gynvael | 1 jesli sie przecina | |
Gynvael | i dodatkowo -1 jesli promien jest wewnatrz obiektu | |
Gynvael | z tego ostatniego narazie korzystac nie bedziemy ;> | |
21:35 | Gynvael | Intersect zwraca rowniez odleglosc od zrodla promienia do miejsca przeciecia |
Gynvael | (dzieki temu wykonujac proste obliczenie zrodlo_promienia + kierunek_promienia * odleglosc bedziemy mogli uzyskac dokladne miejsce przeciecia) | |
21:36 | Gynvael | ok.. po tej petli wiec mamy nastepujace informacje |
Gynvael | 1) czy promien przecial sie z jakims obiektem | |
Gynvael | 2) jak daleko jest do tego obiektu | |
Gynvael | teraz mozemy wykonad jakis pixel shader, czyli jakies obliczenia ktore z roznych czynnikow wylicza kolor danego pixela | |
Gynvael | w tym przypadku jest to bardzo prosty shader | |
21:37 | Gynvael | if(prim) |
Gynvael | color = Vector3D(1.0, 1.0, 1.0); | |
Gynvael | czyli 'Jesli sie z czyms przecielo, zwroc kolor BIALY' | |
21:38 | Gynvael | dobra ;> |
Gynvael | mamy sliczna scene, czarno biala ;p | |
Gynvael | zrobmy troche bardziej zaawansowany shader | |
21:39 | Gynvael | mianowicie wyliczmy kolor na podstawie odleglosci miejsca przeciecia od zrodla promienia |
Gynvael | odleglosc mamy | |
Gynvael | wystarczy cos tam ja podzielic etc i mamy juz kolor | |
Gynvael | http://wyklady.net/ray1/r4.zip | |
Gynvael | rzucmy okiem na ten kod | |
Gynvael | na poczatek skompilujmy go i odpalmy | |
Gynvael | standardowe make test | |
Gynvael | wygenerowany zostanie pliczek dump.tga | |
21:40 | Gynvael | dodam ze do test.cpp dodalem tutaj jeszcze 2 kulki |
Gynvael | tak ze na scenie teraz mamy 3 | |
21:41 | Gynvael | RT.AddSphere(Vector3D(1.0,0.0,0.4), 0.4); |
Gynvael | RT.AddSphere(Vector3D(0.0,0.0,0.5), 0.4); | |
Gynvael | RT.AddSphere(Vector3D(-1.0,0.0,0.6), 0.4); | |
Gynvael | zwroccie uwage ze ta najbardziej po lewej jest najblizej ekranu | |
Gynvael | a ta po prawej najdalej | |
Gynvael | od ekranu | |
Gynvael | e odwrotnie ;p | |
21:42 | Gynvael | |
Gynvael | jak widac kulki zostaly ladnie 'pocieniowane' | |
Gynvael | do czego taki shader (troche pomodyfikowany jeszcze) mogl by sluzyc ? np do efektu mgly ;> | |
Gynvael | ale narazie sie tym nie zajmujmy | |
Gynvael | rzucmy okiem jeszcze na animacje kulki poruszajacej sie po ekranie | |
21:43 | Gynvael | http://wyklady.net/ray1/dist_shader.avi |
Gynvael | przypominam ze DivX 4.XX do tego jest potrzebny | |
Gynvael | animacja zajmuje ponizej 100kb | |
21:44 | Gynvael | widac na niej shader ktory teraz wybralismy w akcji ;> |
Gynvael | ok | |
Gynvael | wszystko ladnie | |
Gynvael | ale troche ubogo | |
Gynvael | zostawmy narazie w spokoju ten shaderek zalezny od odleglosci | |
21:45 | Gynvael | i dorzucmy do engine'u pare istotnych rzeczy |
Gynvael | mianowicie kazdy obiekt ma jakis material | |
Gynvael | co to jest material ? | |
21:46 | Gynvael | jest to zbior cech (takich jak np kolor, odbijalonosc swiatla etc) ktore opisuja dany obiekt |
Gynvael | np mozemy powiedziec ze jakas tam kulka jest zielona i matowa | |
Gynvael | a inna jest niebieska i wypolerowana | |
Gynvael | takie cechy wlasnie zapisuje sie w materiale | |
Gynvael | wprowadzmy takze swiatla | |
21:47 | Gynvael | swiatlo dla nas poki co bedzie to PUNKT w przestrzeni, ktory emanuje swiatlo w danym kolorze |
Gynvael | czyli swiatlo dla nas == punkt + kolor ;> | |
21:48 | Gynvael | teraz omowmy pare rodzajow oddzialywania swiatla na obiekt na scenie |
Gynvael | mamy cos takiego jak: | |
Gynvael | (w naszym przypadku, bo rodzajow swiatel jest wiecej) | |
Gynvael | swiatlo rozproszone - diffusive | |
21:49 | Gynvael | swiatlo refleksyjne - specular - rekleksy/blyski na obiektach powstaja gdy obiekt ma wysokie specular |
Gynvael | Diffusive i Specular | |
Gynvael | to sa cechy materialu | |
Gynvael | przykladowo | |
Gynvael | jesli obiekt jest matowy | |
21:50 | Gynvael | to bedzie mial zerowe Specular (nie beda sie na tym materiale odblyski tworzyc ani refleksy) i Diffusive w granicach np 40% |
Gynvael | a gdy mamy w rece np wypolerowany przez jubilera kamien, to on moze miec wysoki Specular (np 40% to sporo) i Diffusive w granicach 30% | |
Gynvael | za chwile wyjdzie to w praniu | |
Gynvael | ;> | |
21:51 | Gynvael | ok |
Gynvael | a teraz troche kodu | |
Gynvael | http://wyklady.net/ray1/r5.zip | |
Gynvael | rzucmy okiem na poczatku na test.cpp | |
Gynvael | potem skompilujemy | |
Gynvael | a potem omowie nowy shader ktory tu uzylem | |
21:52 | Gynvael | ok |
Gynvael | Material PolishedStone(Vector3D(0.0, 1.0, 0.0), 0.3, 0.4); | |
Gynvael | Material Paper(Vector3D(1.0, 1.0, 0.8), 0.0, 0.5); | |
Gynvael | na poczatek tworze dwa materialy | |
Gynvael | material przyjmuje 3 argumenty | |
Gynvael | Material(Vector3D Color, double Specular, double Diffusive); | |
21:53 | Gynvael | Kolor, wspolczynnik odbijalnosci swiatla i wspolczynnik rozproszenia swiatla ;> |
Gynvael | PolishedStone - wypolerowany kamien - kolor zielony (0,1,0), 30% odbijalnosci (0.3), 40% rozproszenia (0.4) | |
21:54 | Gynvael | Paper - papier - kolor kremowy (1,1,0.8), matowy (0% odbijalnosci), i rozproszenie 50% |
Gynvael | RT.AddLight(Vector3D(2.0, 2.0, -3.0), Vector3D(2.0, 2.0, 2.0)); | |
Gynvael | potem jest dodane swiatlo, niedaleko kamery ;> | |
Gynvael | peirwszy parametr to pozycja | |
Gynvael | drugi to kolor swiatla | |
21:55 | Gynvael | 2.0 2.0 2.0 - bialy bardzo intensywny |
Gynvael | prosze zauwazyc ze to jednoczenie kolor jak i sila swiatla ;> | |
Gynvael | RT.AddSphere(Vector3D(1.0,0.0,0.5), 0.4, PolishedStone); | |
Gynvael | RT.AddSphere(Vector3D(-1.0,0.0,0.5), 0.4, Paper); | |
Gynvael | standardowe AddSphere z dodanym parametrem | |
21:56 | Gynvael | czyli materialem |
Gynvael | ok | |
Gynvael | make test | |
Gynvael | i ogladamy dump.tga | |
Gynvael | ||
21:57 | Gynvael | tak to wyglada ;> |
Gynvael | po lewej mamy kulke-papier | |
Gynvael | po prawej zielona z refleksyjnoscia | |
Gynvael | jak zrozumiec Specular i Diffusive w akcji ? | |
Gynvael | rzucmy okiem na dwie animacje | |
21:58 | Gynvael | http://wyklady.net/ray1/diffusive.avi |
Gynvael | kulka wyrenderowana z diffusive przyjmujacym wartosci od 0 do 1 (patrzcie wskaznik na dole animacji | |
Gynvael | ) | |
Gynvael | http://wyklady.net/ray1/specular.avi | |
21:59 | Gynvael | to samo, tyle ze ze specular |
Gynvael | (kody generujace wszystkie pokazane animacje beda dostepne wraz z logami po wykladzie (pewnie dzien po wykladzie ;p)) | |
22:00 | Gynvael | ok |
Gynvael | a teraz rzucmy okiem na kod | |
Gynvael | zauwazmy po pierwsze ze doszly nowe pliki | |
Gynvael | Material.* | |
Gynvael | prosta klasa opisujaca material | |
Gynvael | oraz Light.* | |
Gynvael | tez prosta klasa opisujaca swiatlo | |
Gynvael | wewnatrz RTEngine.* doszlo | |
Gynvael | std::vector<Light*> LightList; | |
Gynvael | czyli lista swiatel | |
22:01 | Gynvael | oraz funkcja |
Gynvael | void AddLight(Vector3D Position, Vector3D Color); | |
Gynvael | ktora robi.. wiadomo co | |
Gynvael | ;> | |
Gynvael | jako ze mamy zupelnie nowy shader w Trace | |
Gynvael | to rzucmy na niego okiem | |
22:02 | Gynvael | RTEngine.cpp funkcja Vector3D RTEngine::Trace(Ray ray) |
Gynvael | petla znajdujaca obiekt jest bez zmian | |
Gynvael | shader - distance zostal zakomentowany | |
22:03 | Gynvael | po nim mamy: |
Gynvael | Vector3D pi = ray.Origin + ray.Direction * dist; // point of intersection | |
Gynvael | pi - punkt przeciecia | |
Gynvael | za pomoca rownania o ktorym mowilem wczesniej wyliczamy dokladny punkt przeciecia | |
Gynvael | Vector3D prim_color = prim->m.Color; | |
Gynvael | pobieramy kolor obiektu | |
Gynvael | a nastepnie mamy petle | |
Gynvael | vector<Light*>::iterator lightiter; | |
Gynvael | for(lightiter = this->LightList.begin(); lightiter != this->LightList.end(); lightiter++) | |
Gynvael | { | |
Gynvael | [...] | |
22:04 | Gynvael | } |
Gynvael | ktora mozna okreslic jako | |
Gynvael | 'Dla kazdego swiatla na scenie...' | |
Gynvael | (przypominam ze wytlumaczenie od strony matematycznej wzorow uzywanych w RT bedzie na drugim wykladzie za tydzien) | |
22:05 | Gynvael | po czym mamy dwa kawalki kodu |
Gynvael | if(prim->m.Diffusive > 0.0) | |
Gynvael | { | |
Gynvael | double dot = L.Dot(N); | |
Gynvael | if (dot > 0.0l) | |
Gynvael | { | |
Gynvael | double diff = dot * prim->m.Diffusive; | |
Gynvael | color += ((*lightiter)->Color * prim_color) * diff; | |
Gynvael | } | |
Gynvael | } | |
Gynvael | ten oblicza czesc odpowiedzialna za swiatlo rozproszone | |
Gynvael | if(prim->m.Specular > 0.0) | |
Gynvael | { | |
Gynvael | Vector3D R = L - N * L.Dot(N) * 2.0l; | |
Gynvael | double dot = ray.Direction.Dot(R); | |
Gynvael | if (dot > 0) | |
Gynvael | { | |
Gynvael | dot = pow(dot, 20.0); // dot *= dot; dot *= dot; dot *= dot; dot *= dot; | |
Gynvael | double spec = dot * prim->m.Specular; | |
Gynvael | color += (*lightiter)->Color * spec; | |
Gynvael | } | |
Gynvael | } | |
Gynvael | a ten za swiatlo refleksyjne | |
22:06 | Gynvael | przed nimi jest wspolny kawalek kodu |
22:10 | DJ_cool_ | Gynvael: 22:06 <@Gynvael> przed nimi jest wspolny kawalek kodu |
22:11 | Gynvael | thx ;> |
22:12 | Gynvael | Vector3D L = (*lightiter)->Position - pi; |
Gynvael | L.Norm(); | |
Gynvael | Vector3D N = prim->Normal(pi); | |
Gynvael | jest tutaj funkcja o ktorej nie wspomnialem | |
Gynvael | prim->Normal(pi) | |
Gynvael | jest to funkcja, ktora MUSI posiadac kazdy obiekt (patrz Primitives.h) | |
Gynvael | funckja ta przyjmuje punkt przeciecia | |
Gynvael | i ma zwrocic WEKTOR NORMALNY dla obiektu w tym miejscu | |
22:13 | Gynvael | co to jest wektor normalny ? w duzym skrocie jest to wektor pokazujacy 'w ktorym kierunku obiekt jest zwrocony' |
Gynvael | (tj gdzie jest jego przod) | |
22:15 | Gynvael | w przypadku kuli lub kola |
Gynvael | jest ten wektor bardzo prostu uzyskac | |
Gynvael | mianowicie ten wektor jest rowny wektorowi poprowadzonymi od srodka kuli/kola do punktu przeciecia | |
Gynvael | np | |
Gynvael | ||
22:16 | Gynvael | tam sa pokazane 3 normale (nieznormalizowane ;p) dla 3ch roznych punktow kola |
Gynvael | po co nam normal ? | |
Gynvael | dzieki iloczynowi skalarnemu normala oraz promienia | |
Gynvael | mozemy ustalic pod jakim kontem pada swiatlo w dane miejsce | |
22:17 | Gynvael | (dokladnie o matematyce bedzie na nastepnym wykladzie jak juz mowilem ;>) |
Gynvael | *katem (rany ;p) | |
22:18 | Gynvael | ok ;> mamy dwa rodzaje swiatel |
Gynvael | rzucmy okiem na animacje 3ch kul ;> | |
Gynvael | http://wyklady.net/ray1/anim1.avi | |
Gynvael | (211kb) | |
22:19 | Gynvael | juz jakos zaczyna to wygladac |
Gynvael | mamy juz dwa rodzaje dzialania swiatla | |
Gynvael | na obiekt | |
Gynvael | dodajmy jeszcze na koniec tej czesci wykladu odbicia | |
Gynvael | tj odbicia lustrzane | |
22:20 | Gynvael | tak zeby jedna kula mogla 'przejrzec sie w drugiej' |
Gynvael | http://wyklady.net/ray1/r6.zip | |
Gynvael | co nowego potrzebujemy ? | |
Gynvael | po pierwsze w materiale, informacji na temat jak bardzo obiekt jest lustrzany | |
Gynvael | double Reflective; | |
22:21 | Gynvael | standardowo, od 0.0 do 1.0 |
Gynvael | skompilujmy sobie i odpalmy | |
Gynvael | standardowo juz powstanie pliczek dump.tga ;> | |
22:22 | Gynvael | |
Gynvael | jako ze lewa kulka jest matowa | |
Gynvael | to nic nie odbija | |
22:23 | Gynvael | natomiast po lewej stronie prawej kulki |
Gynvael | widac odbicie lewej ;> | |
Gynvael | rzucmy okiem na kolejna animacje ktora prezentuje wplyw wspolczynnika odbicia na wyglad sceny | |
Gynvael | http://wyklady.net/ray1/refl.avi | |
22:24 | Gynvael | a teraz zastanowmy sie jak uzyskac w raytracingu informacje na temat tego co jest odbite w danym miejscu |
Gynvael | sprawa wyglada dosc prosto | |
22:25 | Gynvael | mianowicie musimy stworzyc i wystrzelic nowy promien |
Gynvael | z miejsca odbicia, w kierunku gdzie odbity promien by polecial (tj odbijamy promien oryginalny od obiektu, tak jak na fizyce nas uczono ;>) | |
Gynvael | dostajemy dzieki temu informacje zwrotna z kolorem w co ten 'odbity promien' trafil | |
22:26 | Gynvael | i ten kolor mozemy pomnozyc przez wspolczynnik odbicia |
Gynvael | i dodac do koloru wyjsciowego | |
Gynvael | double refl = prim->m.Reflective; | |
Gynvael | if(refl > 0.0 && refl_depth < 4) | |
Gynvael | { | |
Gynvael | Vector3D N = prim->Normal(pi); | |
Gynvael | Vector3D R = ray.Direction - N * (ray.Direction.Dot(N) * 2.0); | |
Gynvael | Vector3D rcol( 0, 0, 0 ); | |
Gynvael | double dist; | |
Gynvael | Ray tempr(pi + R * 0.0001, R); | |
22:27 | Gynvael | rcol = Trace(tempr, refl_depth + 1); |
Gynvael | color += rcol * refl * prim_color; | |
Gynvael | } | |
Gynvael | jak widac funkcji Trace doszedl dodatkowy parametr, ktory mowi o zaglebieniu rekurencyjnym | |
Gynvael | w tym wypadku pilnujemy po prostu zeby wiecej niz 4ry zaglebienia odbicia nie byly ;> | |
Gynvael | w sumie standardowa rekurencja, nic wiecej | |
22:28 | Gynvael | dobra ;> |
Gynvael | rzucmy okiem na animacje z odbiciami | |
Gynvael | http://wyklady.net/ray1/anim2.avi | |
22:29 | Gynvael | i to w sumie tyle jesli chodzi o dzisiejszy wyklad ;> |
Gynvael | zapraszam do glosowania i komentowania -> http://forum.wyklady.net/index.php?topic=77.0 | |
Gynvael | jeszcze 3 slowa na temat tego co bedzie na nastepnym wykladzie | |
22:30 | Gynvael | przede wszystkim postaram sie wyjasnic te wzorki matematyczne |
Gynvael | intersekcje etc | |
Gynvael | dlaczego swiatlo obliczamy tak a nie inaczej etc | |
Gynvael | po za tym powiem tez o cieniach, | |
Gynvael | wprowadze do silnika inne obiekty - np trojkaty | |
22:31 | Gynvael | pojawi sie tez teksturowanie (z i bez filtrowania) |
Gynvael | materialy per vertex w trojkatach | |
Gynvael | a na koncu mapy materialow (czyli wlasnie textury, mapy refleksji, bumpmapy, etc etc) | |
22:32 | Gynvael | beda tez 3 slowa o optymalizacji |
Gynvael | czyli zamiast listy -> octtree ;> | |
Gynvael | i w sumie tyle ;> | |
Gynvael | dziekuje za przybycie ;> i zapraszam na nastepna czesc (termin na dniach pojawi sie w terminarzu) |