„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés
| 554. sor: | 554. sor: | ||
== A harmadik házihoz szükséges elmélet == | == A harmadik házihoz szükséges elmélet == | ||
A harmadik házinál a programod teljes mértékben a CPU-n fog futni, ami a videókártya segítsége nélkül nagyon meg fog izzadni, hogy a képet előállítsa neked. Az előző házikkal ellentétben itt az optimalizálás nagyon sokat segít, egy release build (-O3) és egy debug build (-O0) között akár több mint ötszörös sebességkülönbség is lehet. Ezért, ha éppen nem debuggolsz, mindenképpen release buildet fordíts. | |||
=== A Sugárkövetés alapjai === | === A Sugárkövetés alapjai === | ||
A második házihoz szükséges példaprogramok között volt egy 3D-s is. Most ezzel a témakörrel fogunk foglalkozni. Technikailag abban a példaprogramban a 3D rajzolást a GLUT csinálta helyettünk. De mielőtt belemennénk a részletekbe, hogy pontosan mit is csinált (ezt majd a 4. háziba), vegyük észre, mi is ki tudunk rajzolni egy olyan kockát, mint amit a GLUT csinált, akár az OpenGL segítsége nélkül is. | |||
Először gondoljuk át hogy a valóságban hogyan csinálnánk képet egy kockáról. Szükségünk van egy fényforrásra, enélkül garantáltan nem látnánk semmit, és szükségünk van egy ernyőre is (pl: retina), amin a képet felfoghatjuk. Továbbá nem árt, ha van egy kocka is, amit lefényképezhetünk. | |||
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. | |||
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: | |||
<br/> <syntaxhighlight lang="c"> | <br/> <syntaxhighlight lang="c"> | ||
| 579. sor: | 579. sor: | ||
</syntaxhighlight> <br/> | </syntaxhighlight> <br/> | ||
A kamera megvalósítása már egy picit trükkösebb. Az implementálása az alábbi lépésekből áll: | |||
* Meg kell adnunk a képzeletbeli kamera pozícióját. Kódban pl: <code>pos</code>. | |||
* Meg kell adnunk, hogy a kamera, merrefelé néz. Kódban pl: <code>fwd</code> (egységvektor). | |||
* Azt is tudnunk kell, hogy melyik iránynak felel meg a felfele ("What's up?"). Kódban pl nevezzük <code>up</code>-nak. | |||
* 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>. | |||
* 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>. | |||
* 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: | |||
<br/> <syntaxhighlight lang="c"> | <br/> <syntaxhighlight lang="c"> | ||
| 608. sor: | 608. sor: | ||
</syntaxhighlight> <br/> | </syntaxhighlight> <br/> | ||
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. | |||
* 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) | |||
* 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 fölső 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: | |||
<br/> <syntaxhighlight lang="c"> | <br/> <syntaxhighlight lang="c"> | ||
| 648. sor: | 649. sor: | ||
</syntaxhighlight> <br/> | </syntaxhighlight> <br/> | ||
Most már mindent tudunk a sugárkövetésről, azt leszámítva, hogy hogyan kell egy sugarat követni. | |||
=== Hogyan kövessük a sugarakat? === | === Hogyan kövessük a sugarakat? === | ||