„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés
a typo |
a Házi sorszámok helyett most már témakörökre hivatkozok (az talán ritkábban változik, mint a házik száma) |
||
| 34. sor: | 34. sor: | ||
** <code>onIdle()</code> - Ez a függvény az idő múlását hivatott jelezni, így itt kell kezelni mindent ami az időtől függ (animáció). | ** <code>onIdle()</code> - Ez a függvény az idő múlását hivatott jelezni, így itt kell kezelni mindent ami az időtől függ (animáció). | ||
== | == 2D OpenGL == | ||
=== Rajzolás az OpenGL segítségével === | === Rajzolás az OpenGL segítségével === | ||
| 259. sor: | 259. sor: | ||
http://i.imgur.com/ezFQ4l4.png | http://i.imgur.com/ezFQ4l4.png | ||
=== Koordináta rendszerek === | === Koordináta rendszerek === | ||
Az | Az korábbi példákban valószínűleg feltűnt, hogy a pontok NDC (normalizált eszköz koordináta) megadása nem túl kényelmes, még akkor se ha, a világnak mindig ugyanazt a részét nézzük. De mit tegyük akkor, ha a képzeletbeli kamera amivel "lefényképezzük" a jelenetet mozoghat, sőt akár még foroghat is. Az OpenGL kitalálóinak az ötlete az volt erre, hogy a kamera mindig maradjon egy helyben, de ha pl. balra akarnánk forgatni, akkor helyette inkább a világ forogjon jobbra, a kamera viszont maradjon ugyanott, ezzel is ugyanazt a hatást érjük el. Első ránézésre nem látszik, hogy ez miért jó, de ez valójában egy nagyon jó ötlet. Szerencsére ha a világot el kell forgatnunk, akkor nem kell minden egyes pontot nekünk külön-külön elforgatni egy ronda trigonometrikus képlet alapján, ezt rábízhatjuk az OpenGL-re is, hogy mindig mielőtt rajzolna, azelőtt végezzen el valamilyen transzformációt a pontokat amiket kapott. | ||
A grafikában általában affin (egyenestartó) transzformációkat szoktunk használni, a vetítéseket leszámítva. Ezeket a transzformációkat a legkényelmesebb a mátrixuk segítségével tudjuk megadni, több egymás utáni transzformáció pedig egyszerűen a mátrixok szorzatát jelenti. Viszont fontos megjegyezni, hogy 3D-ben az eltolás nem lineáris transzformáció, és nem is lehet mátrixszal felírni. Pedig erre a műveletre biztos, hogy szükségünk lesz. Ennek kiküszöbölésére használhatunk 4D-be 'w' = 1 koordinátával beágyazott 3D-s koordináta-rendszert, ahol az eltolás is egy lineáris trafó. | A grafikában általában affin (egyenestartó) transzformációkat szoktunk használni, a vetítéseket leszámítva. Ezeket a transzformációkat a legkényelmesebb a mátrixuk segítségével tudjuk megadni, több egymás utáni transzformáció pedig egyszerűen a mátrixok szorzatát jelenti. Viszont fontos megjegyezni, hogy 3D-ben az eltolás nem lineáris transzformáció, és nem is lehet mátrixszal felírni. Pedig erre a műveletre biztos, hogy szükségünk lesz. Ennek kiküszöbölésére használhatunk 4D-be 'w' = 1 koordinátával beágyazott 3D-s koordináta-rendszert, ahol az eltolás is egy lineáris trafó. | ||
| 561. sor: | 558. sor: | ||
http://i.imgur.com/C1iKaHx.gif | http://i.imgur.com/C1iKaHx.gif | ||
== | == Sugárkövetés == | ||
A | A sugárkövetős 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ő feladatokkal 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 | A 2D OpenGL-es 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 3D OpenGL résznél), 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. | 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. | ||
| 667. sor: | 664. sor: | ||
</syntaxhighlight> <br/> | </syntaxhighlight> <br/> | ||
Megjegyzés: én általában nem ilyen kamerát szoktam használni, de ez ugyanazt az eredményt adja, mint amit a | Megjegyzés: én általában nem ilyen kamerát szoktam használni, de ez ugyanazt az eredményt adja, mint amit a 3D OpenGL résznél fogunk kapni, a gluLookAt() függvény segítségével. | ||
Most már mindent tudunk a sugárkövetésről, azt leszámítva, hogy hogyan kell egy sugarat követni. | Most már mindent tudunk a sugárkövetésről, azt leszámítva, hogy hogyan kell egy sugarat követni. | ||
| 1 486. sor: | 1 483. sor: | ||
=== A kétirányú sugárkövetés === | === A kétirányú sugárkövetés === | ||
A globális illumináció implementálásakor sokak fájó szívvel válnak meg a kódban a lokális illumináció résztől, lévén, hogy hiába írták meg, ha nem jó semmire. De ez nem így van. Egyrészt a | A globális illumináció implementálásakor sokak fájó szívvel válnak meg a kódban a lokális illumináció résztől, lévén, hogy hiába írták meg, ha nem jó semmire. De ez nem így van. Egyrészt a 3D OpenGL-es házikhoz nagyon nagy segítséget fog nyújtani, hogy érted, hogy hogyan működik a lokális illumináció, hiszen az OpenGL is ezt fogja használni. Másrészt még a sugárkövetés házi végleges formájába is hasznos lehet az a kód. | ||
A kétirányú sugárkövetés ötlete, hogy használjuk a lokális és a globális illuminációt egyszerre. A diffúz anyagot világítsuk meg lokálisan, és csak azok a fotonok keltsenek rajta kausztikát, amik nem triviális úton (egyenes vonalon, végig a levegőben, kölcsönhatás nélkül) jutottak el a fényforrásból az anyagig. Tehát ha a foton ütközésekor a rekurziós szint 0, akkor az közvetlenül a fényforrásból jutott el hozzánk, azt ne mentsük el. | A kétirányú sugárkövetés ötlete, hogy használjuk a lokális és a globális illuminációt egyszerre. A diffúz anyagot világítsuk meg lokálisan, és csak azok a fotonok keltsenek rajta kausztikát, amik nem triviális úton (egyenes vonalon, végig a levegőben, kölcsönhatás nélkül) jutottak el a fényforrásból az anyagig. Tehát ha a foton ütközésekor a rekurziós szint 0, akkor az közvetlenül a fényforrásból jutott el hozzánk, azt ne mentsük el. | ||
| 1 559. sor: | 1 556. sor: | ||
<br/> | <br/> | ||
== | == 3D OpenGL == | ||
=== Kedvcsináló === | === Kedvcsináló === | ||
| 1 573. sor: | 1 570. sor: | ||
Csak emlékeztetőül, sugárkövetéssel 14 háromszög nem ment valós időben... A videókártya segítségével a 72 millió háromszög real-time kirajzolása még messze nem a maximum amit el lehet érni, a nagyházim kódja egyáltalán nincs is optimalizálva. | Csak emlékeztetőül, sugárkövetéssel 14 háromszög nem ment valós időben... A videókártya segítségével a 72 millió háromszög real-time kirajzolása még messze nem a maximum amit el lehet érni, a nagyházim kódja egyáltalán nincs is optimalizálva. | ||
A videókártyát használhatnánk arra, hogy gyors raytracert írjunk. Ehhez viszont meg kellene tanulni, hogy hogyan kell a videókártyát programozni... Ehelyett mi a videókártya kezelését az OpenGLre bízzuk, és az inkrementális elvet használva fogunk rajzolni (ugyanúgy, mint | A videókártyát használhatnánk arra, hogy gyors raytracert írjunk. Ehhez viszont meg kellene tanulni, hogy hogyan kell a videókártyát programozni... Ehelyett mi a videókártya kezelését az OpenGLre bízzuk, és az inkrementális elvet használva fogunk rajzolni (ugyanúgy, mint a 2D OpenGL-es résznél, csak 3D-ben). | ||
=== A 3D-s kocka === | === A 3D-s kocka === | ||
A | A 2D OpenGL-es résznél mutattam egy glut függvényt, ami egy kockát rajzol ki. Ezt a házikhoz nem lehet használni, inkább írjunk meg magunknak. | ||
<br/> <syntaxhighlight lang="c"> | <br/> <syntaxhighlight lang="c"> | ||
| 1 623. sor: | 1 620. sor: | ||
A függvény amit használhatunk az a <code> gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)</code>. Megjegyzések a paramétérek megválasztásához: | A függvény amit használhatunk az a <code> gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)</code>. Megjegyzések a paramétérek megválasztásához: | ||
* A fov-ot szögben, nem radiánba kell megadnunk. A reális értéke 40 - 150 fok között mozog. | * A fov-ot szögben, nem radiánba kell megadnunk. A reális értéke 40 - 150 fok között mozog. | ||
* Az ascpect a képernyő szélessége / képernyő magassága. A házikba | * Az ascpect a képernyő szélessége / képernyő magassága. A házikba ennek az értéke egy. | ||
* A zNear-nél sokan nagy késztetést éreznek, hogy egy nagyon kicsi értéket állítsanak be, hogy semmi se kerüljön az első vágósík elé. A gond viszont ezzel az, hogy az mélység számító algoritmus a természeténél fogva több különböző ponthoz is ugyanazt az értéket rendeli, hiszen csak egy véges tartományt használhat (általában egy 24 bites fixpontos számot). Amiért ez zavaró, az az, hogy minél nagyobb a zFar / zNear értéke, annál nagyobb tartományt kell ugyanarra a (0..1) intervallumra transzformálni. Ez pedig azzal jár hogy az egyre távolabb lévő pontokhoz is ugyanazt a mélységet fogja használni. Ilyenkor pedig nem tudjuk eldönteni, hogy mi látszódik, és mi nem, aminek szinte mindig nagyon ronda eredménye szokott lenni. Egy ökölszabály, hogy a zFar / zNear értéke ne legyen (sokkal) nagyobb 1000-nél. A házikhoz tipikusan nincs szükség 100-nál nagyobb zFar-ra, ilyenkor a zNear ne legyen 0.1-nél kisebb. | * A zNear-nél sokan nagy késztetést éreznek, hogy egy nagyon kicsi értéket állítsanak be, hogy semmi se kerüljön az első vágósík elé. A gond viszont ezzel az, hogy az mélység számító algoritmus a természeténél fogva több különböző ponthoz is ugyanazt az értéket rendeli, hiszen csak egy véges tartományt használhat (általában egy 24 bites fixpontos számot). Amiért ez zavaró, az az, hogy minél nagyobb a zFar / zNear értéke, annál nagyobb tartományt kell ugyanarra a (0..1) intervallumra transzformálni. Ez pedig azzal jár hogy az egyre távolabb lévő pontokhoz is ugyanazt a mélységet fogja használni. Ilyenkor pedig nem tudjuk eldönteni, hogy mi látszódik, és mi nem, aminek szinte mindig nagyon ronda eredménye szokott lenni. Egy ökölszabály, hogy a zFar / zNear értéke ne legyen (sokkal) nagyobb 1000-nél. A házikhoz tipikusan nincs szükség 100-nál nagyobb zFar-ra, ilyenkor a zNear ne legyen 0.1-nél kisebb. | ||
| 2 188. sor: | 2 185. sor: | ||
Viszont ezzel nehézkes a debuggolás, nem lehet igazán jól körülnézni, és valós játékokban is ritka az ennyire egyszerű kamera. | Viszont ezzel nehézkes a debuggolás, nem lehet igazán jól körülnézni, és valós játékokban is ritka az ennyire egyszerű kamera. | ||
Én egy egyszerű szabadon-repülő kamera egy implementációjához adok ötletet, | Én egy egyszerű, szabadon-repülő kamera egy implementációjához adok ötletet, ami sokat segíthet a debuggoláshoz (sokszor csak egy nézetből nézve egy jelenetet nem lehet eldönteni, hogy az jó-e). | ||
Kétféle input érdekel minket, a billentyűlenyomások (W,A,S,D), és az egér mozgatása (úgy, hogy közbe az egyik egérgomb le van nyomva). | Kétféle input érdekel minket, a billentyűlenyomások (W,A,S,D), és az egér mozgatása (úgy, hogy közbe az egyik egérgomb le van nyomva). | ||