„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés
| 565. sor: | 565. sor: | ||
Ha pontosan azt akarnánk lemodellezni, ahogy a valóságban a kép keletkezik, akkor gondba lennénk, mert a számítógép teljesítményéhez képest gyakorlatilag végtelen fotonnal kéne dolgoznunk. És ráadásul a fényforrásból kiinduló fotonok döntő többsége még csak nem is megy az ernyőnek a közelébe se. Ezt kiaknázandó, a sugárkövetés egyik alapötlete, hogy az ernyőből induljuk ki, ne a fényforrásból, és megfordított irányú fotonokat kövessünk, így csak a releváns fény részecskékkel fogunk foglalkozni. | Ha pontosan azt akarnánk lemodellezni, ahogy a valóságban a kép keletkezik, akkor gondba lennénk, mert a számítógép teljesítményéhez képest gyakorlatilag végtelen fotonnal kéne dolgoznunk. És ráadásul a fényforrásból kiinduló fotonok döntő többsége még csak nem is megy az ernyőnek a közelébe se. Ezt kiaknázandó, a sugárkövetés egyik alapötlete, hogy az ernyőből induljuk ki, ne a fényforrásból, és megfordított irányú fotonokat kövessünk, így csak a releváns fény részecskékkel fogunk foglalkozni. | ||
A másik alapötlet, hogy a fotonok olyan sokan vannak, hogy a Nagy Számok Törvénye alapján gyakorlatilag teljesen pontos becsléseket kaphatunk a fotonok viselkedéséről, anélkül, hogy azokkal egyesével foglalkoznunk kellene. Ezt felhasználva | A másik alapötlet, hogy a fotonok olyan sokan vannak, hogy a Nagy Számok Törvénye alapján gyakorlatilag teljesen pontos becsléseket kaphatunk a fotonok viselkedéséről, anélkül, hogy azokkal egyesével foglalkoznunk kellene. Ezt felhasználva nagy mennyiségű fotonból álló csomagok, úgynevezett sugarak útját követjük, és nem fotonokét. Ez talán megmagyarázza, hogy miért hívjuk a technikát sugárkövetésnek. A sugárkövetéshez szükségünk van egy képzeletbeli kamerára, és egy ernyőre (egy téglalapra). A téglalapot felosztjuk annyi egyenlő részre, ahány pixelből áll az ablakunk. Jelen esetben, 600x600-as ablak esetében ez azt jelenti, hogy a téglalap négyzet lesz(két egység élhosszúságú). Ezek után az ablak minden egyes pixelére azt a színt rajzoljuk ki, amit a képzeletbeli kamera látna az ernyőnek a pixelhez tartozó részén keresztül. | ||
Az OpenGL használata nélkül ezt úgy kivitelezhetnénk, hogy képet mint egy színekből álló tömböt eltároljuk magunknak, abba renderelünk, majd valamilyen megfelelő kép formátumába kiírjuk ezt egy fájlba. Ezt a megoldást viszont nem lenne túl kényelmes használni. De az OpenGL-t is megkérhetjük arra, hogy jelenítse meg a képet, amit lerendereltünk a <code>glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)</code> függvény segítségével. A házikban tipikusan az utóbbi megoldást szoktuk használni. Például egy lehetséges megvalósítása: | Az OpenGL használata nélkül ezt úgy kivitelezhetnénk, hogy képet mint egy színekből álló tömböt eltároljuk magunknak, abba renderelünk, majd valamilyen megfelelő kép formátumába kiírjuk ezt egy fájlba. Ezt a megoldást viszont nem lenne túl kényelmes használni. De az OpenGL-t is megkérhetjük arra, hogy jelenítse meg a képet, amit lerendereltünk a <code>glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)</code> függvény segítségével. A házikban tipikusan az utóbbi megoldást szoktuk használni. Például egy lehetséges megvalósítása: | ||
| 586. sor: | 586. sor: | ||
* Tegyük fel, hogy téglalap (vagy sík) egységnyi távolságra van a kamerától. Ekkora annak a középpontja: <code>pos + fwd</code>. | * Tegyük fel, hogy téglalap (vagy sík) egységnyi távolságra van a kamerától. Ekkora annak a középpontja: <code>pos + fwd</code>. | ||
* Tudnunk kell még, hogy melyik irány van jobbra. Ezt az előre és a felfele pozícióból ki tudjuk számolni: <code>right = cross(fwd, up)</code>. | * Tudnunk kell még, hogy melyik irány van jobbra. Ezt az előre és a felfele pozícióból ki tudjuk számolni: <code>right = cross(fwd, up)</code>. | ||
* A felfele vektor amit megadtunk nem biztos, hogy merőleges az előre vektorra, pedig nekünk olyanra van szükségünk. Pl: ha rézsútosan előre és lefele nézünk, de az 'up' vektor az ég fele mutat. Ez | * A felfele vektor amit megadtunk nem biztos, hogy merőleges az előre vektorra, pedig nekünk olyanra van szükségünk. Pl: ha rézsútosan előre és lefele nézünk, de az 'up' vektor az ég fele mutat. Ez valójában nem baj, mert a jobbra és előre vektor ismeretében már ki tudjuk számolni a pontos felfele vektort: <code>up = cross(right, fwd)</code>. | ||
* Ha ezek megvannak, akkor ki kell tudnunk számolni, hogy egy (x, y) koordinátájú pixelnek a téglalap (ami most egy egység oldalhosszúságú négyzet) melyik része felel meg. Ezt így tehetjük meg: | * Ha ezek megvannak, akkor ki kell tudnunk számolni, hogy egy (x, y) koordinátájú pixelnek a téglalap (ami most egy 2 egység oldalhosszúságú négyzet) melyik része felel meg. Ezt így tehetjük meg: | ||
<br/> <syntaxhighlight lang="c"> | <br/> <syntaxhighlight lang="c"> | ||
| 600. sor: | 600. sor: | ||
Vector plane_intersection = plane_pos + pos_on_plane.x * right + pos_on_plane.y * up; | Vector plane_intersection = plane_pos + pos_on_plane.x * right + pos_on_plane.y * up; | ||
</syntaxhighlight> <br/> | </syntaxhighlight> <br/> | ||
Erről egy nem túl méretarányos ábra, ami a megértést talán segíti: | |||
http://i.imgur.com/gERYzPV.png | |||
* És innen már tudunk mindent a sugárról, amit követnünk kell. Ezeket az adatok célszerű egy struktúrába zárni: | * És innen már tudunk mindent a sugárról, amit követnünk kell. Ezeket az adatok célszerű egy struktúrába zárni: | ||
<br/> <syntaxhighlight lang="c"> | <br/> <syntaxhighlight lang="c"> | ||
| 609. sor: | 614. sor: | ||
Megjegyzések az algoritmussal kapcsolatban: | Megjegyzések az algoritmussal kapcsolatban: | ||
* Az ernyő (a téglalap) az, amin a kép keletkezik, az viselkedik úgy mint a szemünk. Ha | * Az ernyő (a téglalap) az, amin a kép keletkezik, az viselkedik úgy mint a szemünk. Ha az ernyő helyére állnánk, akkor látnánk ugyan azt a képet, mint amit meg fogunk jeleníteni. Ezért célszerű kezdetben a kamera pozíciója helyett az ernyő pozícióját megadni. A kamera pozíciója amúgy irreleváns, az tetszőlegesen távol lehet a téglalaptól. Ha a távolsággal arányosan növeljük a téglalap méretét, akkor ugyan azt a képet fogjuk kapni. | ||
* Azzal, hogy kijelentettük, hogy téglalap egység négyzet, és egységnyi távolságra van a kamerától, implicit kimondtuk, hogy a kamera látószöge arctg(1) = | * Azzal, hogy kijelentettük, hogy téglalap egy 2 egység élhosszúságú négyzet, és egységnyi távolságra van a kamerától, implicit kimondtuk, hogy a kamera látószöge 2*arctg(1) = 90 fok. Egy nem túl arányos rajz arról, hogy ez hogy jött ki: | ||
* Ha teljesen korrektek akarnánk lenni, akkor fél pixellel el kéne tolni | |||
http://i.imgur.com/gC7f6l1.png | |||
De nem biztos, hogy ennyit szeretnénk, úgyhogy a látószög (Field of View - Fov) is legyen inkább paraméter. A kamera-ernyő távolságot célszerűbb változtatni, mint az ernyő méretét, mert így nem kell eltárolni a FoV-ot. Az arány amit akarunk az ctg(fov/2) | |||
* Ha teljesen korrektek akarnánk lenni, akkor fél pixellel el kéne tolni az ernyőt metsző pontokat, hogy azok ne a pixelek bal alsó sarkán keresztül haladjanak át, hanem a közepükön. Bár én szabad szemmel nem látok különbséget ez a változtatás után. | |||
Ezeket a változtatásokat is felhasználva egy lehetséges megvalósítás: | Ezeket a változtatásokat is felhasználva egy lehetséges megvalósítás: | ||
| 620. sor: | 629. sor: | ||
Camera(float fov, const Vector& eye, const Vector& target, const Vector& plane_up) | Camera(float fov, const Vector& eye, const Vector& target, const Vector& plane_up) | ||
: pos(eye - (target-eye).normalize() / | : pos(eye - (target-eye).normalize() / tan((fov*M_PI/180)/2)), plane_pos(eye) | ||
{ | { | ||
Vector fwd = (plane_pos - pos).normalize(); | Vector fwd = (plane_pos - pos).normalize(); | ||