„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés

Rohamcsiga (vitalap | szerkesztései)
Rohamcsiga (vitalap | szerkesztései)
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 igazándiból 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 síkra (téglalapra), amit ernyőként használhatunk. 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. Ezek után az ablak minden egyes pixelére azt a színt rajzoljuk ki, amit a képzeletbeli kamera látna a téglalapnak a pixelhez tartozó részén keresztül.
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 igazándiból 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>.
* 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 a téglalap 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 a téglalap 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.
* 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) = 45 fok. 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-téglalap távolságot célszerűbb változtatni, mint a téglalap méretét, mert így nem kell eltárolni a FoV-ot. Az arány amit akarunk az 0.5*ctg(fov/2)
* 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 a síkot 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.  
 
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() / (2*tan((fov*M_PI/180)/2))), plane_pos(eye)  
       : 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();