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

Rohamcsiga (vitalap | szerkesztései)
 
(17 közbenső módosítás, amit 5 másik szerkesztő végzett, nincs mutatva)
31. sor: 31. sor:
** <code>onKeyboardUp()</code> - Itt tudjuk kezelni egy billentyű felengedést. Valós játékokban ez nagyon hasznos tud lenni, de házikhoz ezt szinte soha nem kell használni.
** <code>onKeyboardUp()</code> - Itt tudjuk kezelni egy billentyű felengedést. Valós játékokban ez nagyon hasznos tud lenni, de házikhoz ezt szinte soha nem kell használni.
** <code>onMouse()</code> - Itt kapunk értesítést arról, ha valamelyik egérgomb állapota megváltozott, és azt is megtudjuk, hogy az ekkor az egér az ablak koordinátái szerint hol volt.
** <code>onMouse()</code> - Itt kapunk értesítést arról, ha valamelyik egérgomb állapota megváltozott, és azt is megtudjuk, hogy az ekkor az egér az ablak koordinátái szerint hol volt.
** <code>onMouseMotion()</code> - Itt tudjuk meg, ha a felhasználó lenyomott egér gomb mellett mozgatta az egeret. A koordinálta értékére ugyan az vonatkozik, mint az onMouse esetén.
** <code>onMouseMotion()</code> - Itt tudjuk meg, ha a felhasználó lenyomott egér gomb mellett mozgatta az egeret. A koordinálta értékére ugyanaz vonatkozik, mint az onMouse esetén.
** <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ó).


== Az első házihoz szükséges elmélet ==
== 2D OpenGL ==


=== Rajzolás az OpenGL segítségével ===
=== Rajzolás az OpenGL segítségével ===


Az OpenGL néhány csak néhány típusú primitívet tud rajzolni, ezekből kell építkeznünk. A típusok:
Az OpenGL csak néhány típusú primitívet tud rajzolni, ezekből kell építkeznünk. A típusok:
* Pontok: <code>GL_POINTS</code>
* Pontok: <code>GL_POINTS</code>
* Vonalak: <code>GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP</code>
* Vonalak: <code>GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP</code>
100. sor: 100. sor:
   glVertex2f(center_x, center_y);
   glVertex2f(center_x, center_y);


   for(int i = 0; i <= CIRCLE_RESOLUTION; i++) {
   for(int i = 0; i < CIRCLE_RESOLUTION; i++) {
     float angle = float(i) / CIRCLE_RESOLUTION * 2.0f * M_PI;
     float angle = float(i) / CIRCLE_RESOLUTION * 2.0f * M_PI;
     // Itt a kor paramtetrikus alakjat hasznaljuk: x = x0 + r*cos(t), y = y0 + r * sin(t)
     // Itt a kor paramtetrikus alakjat hasznaljuk: x = x0 + r*cos(t), y = y0 + r * sin(t)
114. sor: 114. sor:
   glVertex2f(center_x, center_y);
   glVertex2f(center_x, center_y);
    
    
   for(int i = 0; i <= CIRCLE_RESOLUTION; i++) {
   for(int i = 0; i < CIRCLE_RESOLUTION; i++) {
     float angle = float(i) / CIRCLE_RESOLUTION * 2.0f * M_PI;
     float angle = float(i) / CIRCLE_RESOLUTION * 2.0f * M_PI;
     glColor3f(0.0f, 0.5f + 0.5f*cos(angle), 0.5f + 0.5f*sin(angle));
     glColor3f(0.0f, 0.5f + 0.5f*cos(angle), 0.5f + 0.5f*sin(angle));
137. sor: 137. sor:


http://i.imgur.com/6yfh7q2.png
http://i.imgur.com/6yfh7q2.png


=== Eseménykezelés ===
=== Eseménykezelés ===
259. sor: 258. sor:


http://i.imgur.com/ezFQ4l4.png
http://i.imgur.com/ezFQ4l4.png
== A második házihoz szükséges elmélet ==


=== Koordináta rendszerek ===
=== Koordináta rendszerek ===


Az első háziba 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 ugyan azt 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 ugyan azt 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.
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ó.
309. sor: 305. sor:
* <code> glScalef(GLfloat x, GLfloat y, GLfloat z); </code>
* <code> glScalef(GLfloat x, GLfloat y, GLfloat z); </code>


A projekciós mátrixot állító függvényekkel ellentétben, ezeket akkor is szoktuk használni, ha a modellezési mátrix nem egységmátrix. Ezen függvényeknek tetszőleges kombinációját lehet használni, de a sorrend nem mindegy. Egy transzformáció meghívásakor annak a mátrixa hozzászorzódik a GL_MODELVIEW mátrixhoz (balról). Emlékeztető: a mátrix szorzás szorzás asszociatív. Ez azt jelenti, hogy két transzformációs mátrix összeszorzása után az eredmény ugyan úgy transzformál egy tetszőleges vektort, mint ha a két mátrixszal külön szoroztuk volna be.
A projekciós mátrixot állító függvényekkel ellentétben, ezeket akkor is szoktuk használni, ha a modellezési mátrix nem egységmátrix. Ezen függvényeknek tetszőleges kombinációját lehet használni, de a sorrend nem mindegy. Egy transzformáció meghívásakor annak a mátrixa hozzászorzódik a GL_MODELVIEW mátrixhoz (balról). Emlékeztető: a mátrix szorzás szorzás asszociatív. Ez azt jelenti, hogy két transzformációs mátrix összeszorzása után az eredmény ugyanúgy transzformál egy tetszőleges vektort, mint ha a két mátrixszal külön szoroztuk volna be.


A transzformációk fordított sorrendben fejtik ki hatásukat, mint ahogy meghívjuk őket, de ez így intuitív, így haladhatunk a hierarchiában föntről lefele, ha nem így lenne, akkor pl. egy autó kirajzolásánál, azzal kéne kezdenünk, hogy megmondjuk, hogy a dísztárcsa a kerékhez képest hogy helyezkedik el, és csak a legvégén mondhatnánk meg, hogy egyáltalán hol van az az autó, aminek a részeiről eddig beszéltünk.
A transzformációk fordított sorrendben fejtik ki hatásukat, mint ahogy meghívjuk őket, de ez így intuitív, így haladhatunk a hierarchiában föntről lefele, ha nem így lenne, akkor pl. egy autó kirajzolásánál, azzal kéne kezdenünk, hogy megmondjuk, hogy a dísztárcsa a kerékhez képest hogy helyezkedik el, és csak a legvégén mondhatnánk meg, hogy egyáltalán hol van az az autó, aminek a részeiről eddig beszéltünk.
342. sor: 338. sor:
Tanulság: általában az eltolás - forgatás - nagyítás sorrendet szeretjük. Ez nem jelenti azt, hogy más sorrendnek ne lenne értelme, vagy egy konkrét problémának ne lehetne egyszerűbb megoldása másmilyen sorrendet használva.
Tanulság: általában az eltolás - forgatás - nagyítás sorrendet szeretjük. Ez nem jelenti azt, hogy más sorrendnek ne lenne értelme, vagy egy konkrét problémának ne lehetne egyszerűbb megoldása másmilyen sorrendet használva.


De egy probléma még felmerül, a transzformációk hatása permanens, azaz, ha egyszer elforgattad a világot, akkor az úgy marad, amíg vissza nem forgatod. Tehát ha egy objektum kirajzolása miatt akarsz használni egy transzformációt akkor a rajzolás után azt mindenképpen, mindig vissza is kell csinálnod. De mi van ha egy összetett objektum kirajzolásához akár több száz transzformáció is kellet? Akkor a végén az összeset egybe vissza kell csinálni? Nincs erre jobb megoldás? A válasz természetesen az, hogy van, ez a megoldás a mátrix stack.
De egy probléma még felmerül, a transzformációk hatása permanens, azaz, ha egyszer elforgattad a világot, akkor az úgy marad, amíg vissza nem forgatod. Tehát ha egy objektum kirajzolása miatt akarsz használni egy transzformációt akkor a rajzolás után azt mindenképpen, mindig vissza is kell csinálnod. De mi van ha egy összetett objektum kirajzolásához akár több száz transzformáció is kellet? Akkor a végén az összeset egyenként vissza kell csinálni? Nincs erre jobb megoldás? A válasz természetesen az, hogy van, ez a megoldás a mátrix stack.


=== Matrix stack ===
=== Matrix stack ===
543. sor: 539. sor:
=== Görbék ===
=== Görbék ===


Görbék alatt grafikában olyan függvényeket értünk, amik diszkrét ponthalmazból folytonos ponthalmazt állítanak elő. Például a Nyugatitól el akarunk jutni az egyetemig, de úgy, hogy közbe megadott sorrendben érinteni akarjuk a három kedvenc kocsmánkat. Az útvonal, amin ezt megtudjuk tenni, az egy görbe. Az öt helyet, amit érinteni akarunk, kontrollpontnak nevezzük a görbék esetében. Nem csak egy ilyen útvonal létezik, mint ahogy a különböző típusú görbéknek is lehet más a kimenete, ugyan az a bemenet mellett.
Görbék alatt grafikában olyan függvényeket értünk, amik diszkrét ponthalmazból folytonos ponthalmazt állítanak elő. Például a Nyugatitól el akarunk jutni az egyetemig, de úgy, hogy közbe megadott sorrendben érinteni akarjuk a három kedvenc kocsmánkat. Az útvonal, amin ezt megtudjuk tenni, az egy görbe. Az öt helyet, amit érinteni akarunk, kontrollpontnak nevezzük a görbék esetében. Nem csak egy ilyen útvonal létezik, mint ahogy a különböző típusú görbéknek is lehet más a kimenete, ugyanaz a bemenet mellett.


A görbéket szinte mindig valamilyen mozgás leírására használjuk. A görbe kimenete egy hely-idő pontpárokból álló halmaz (vagy ezzel ekvivalens egy folytonos függvény aminek az idő a paramétere, és a hely a visszatérési értéke), ami azt jelenti, hogy a görbéből még a sebesség és a gyorsulás is kiolvasható.
A görbéket szinte mindig valamilyen mozgás leírására használjuk. A görbe kimenete egy hely-idő pontpárokból álló halmaz (vagy ezzel ekvivalens egy folytonos függvény aminek az idő a paramétere, és a hely a visszatérési értéke), ami azt jelenti, hogy a görbéből még a sebesség és a gyorsulás is kiolvasható.
557. sor: 553. sor:
A görbéknek a képletét nem szokás fejből tudni, bár általában könnyen levezethetőek. Az a tapasztalat a korábbi házikból, hogy azoknak a görbéknek a képlete benne szokott lenni az előadásdiákba, amiket a házihoz használni kell. Sőt, amikor több görbét kell használni, akkor általában legalább az egyiknek pszeudokóddal meg is szokták adni az implementációját is.   
A görbéknek a képletét nem szokás fejből tudni, bár általában könnyen levezethetőek. Az a tapasztalat a korábbi házikból, hogy azoknak a görbéknek a képlete benne szokott lenni az előadásdiákba, amiket a házihoz használni kell. Sőt, amikor több görbét kell használni, akkor általában legalább az egyiknek pszeudokóddal meg is szokták adni az implementációját is.   
        
        
Példaprogram: [[Média:Grafpp_tcr_gorbe.cpp|Tenziós Catmull-Rom görbe]]
Példaprogram: ''<Törölve, túl sokan másolták>''


http://i.imgur.com/C1iKaHx.gif
http://i.imgur.com/C1iKaHx.gif


== A harmadik házihoz szükséges elmélet ==
== Sugárkövetés ==


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 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 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.
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.
593. sor: 589. sor:
* Azt is tudnunk kell, hogy melyik iránynak felel meg a felfele ("What's up?"). Kódban pl nevezzük <code>up</code>-nak.
* 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>.
* 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) // a cross-product a vektor szorzat angolul, a dot-product pedig a skalár szorzat. </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>.
* 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 2 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:
599. sor: 595. sor:
<br/> <syntaxhighlight lang="c">  
<br/> <syntaxhighlight lang="c">  
Vector pos_on_plane = Vector(
Vector pos_on_plane = Vector(
   (x + 0.5f - Screen::width/2) / (Screen::width/2),
   (x - Screen::width/2) / (Screen::width/2),
   (y + 0.5f - Screen::height/2) / (Screen::height/2),  
   (y - Screen::height/2) / (Screen::height/2),  
   0
   0
);
);
654. sor: 650. sor:
   void capturePixel(float x, float y) {
   void capturePixel(float x, float y) {
     Vector pos_on_plane = Vector(
     Vector pos_on_plane = Vector(
       (x - Screen::width/2) / (Screen::width/2),
       (x + 0.5f - Screen::width/2) / (Screen::width/2),
       (y - Screen::height/2) / (Screen::height/2),  
       (y + 0.5f - Screen::height/2) / (Screen::height/2),  
       0
       0
     );
     );
667. sor: 663. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Megjegyzés: én általában nem ilyen kamerát szoktam használni, de ez ugyan azt az eredményt adja, mint amit a 4. meg 5. háziban fogunk kapni, a gluLookAt() függvény segítségével.
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.
747. sor: 743. sor:
       Nézzük meg az x és y pontra ezt az algoritmust.
       Nézzük meg az x és y pontra ezt az algoritmust.
       A cross(ab, ax), a cross(bc, bx), és a cross(ca, cx) és kifele mutat a  
       A cross(ab, ax), a cross(bc, bx), és a cross(ca, cx) és kifele mutat a  
       képernyőből, ugyan abba az irányba mint a normál vektor. Ezt amúgy a  
       képernyőből, ugyanabba az irányba mint a normál vektor. Ezt amúgy a  
       dot(cross(ab, ax), normal) >= 0 összefüggéssel egyszerű ellenőrizni.
       dot(cross(ab, ax), normal) >= 0 összefüggéssel egyszerű ellenőrizni.
       Az algoritmus alapján az x a háromszög belsejében van.
       Az algoritmus alapján az x a háromszög belsejében van.
854. sor: 850. sor:
==== Az irány fényforrás ====
==== Az irány fényforrás ====


Egy másik fontos fényforrás az irányfényforrás. Ilyen például a Nap. A Nap olyan távol van tőlünk, hogy a Föld felszínén egy adott területen nagyjából teljesen mindegy, hogy hol helyezkedik el egy objektum, a nap mindig ugyan olyan irányból és intenzitással világítja meg. Az irányfényforrás irányának természetesen fontos szerepe van, itt a számolás lényegesen bonyolultabb, mint az ambiens fényforrás esetén. Például egy felülről megvilágított szobában az asztal teteje sokkal világosabb, mint az asztal alja. Ahhoz, hogy ezt a hatást el tudjuk érni, kizárólag egyszerű fizikára van szükségünk. Tegyük fel, hogy egy anyagra két azonos erősségű fénysugár esik, az egyik merőlegesen, a másik theta szögben.
Egy másik fontos fényforrás az irányfényforrás. Ilyen például a Nap. A Nap olyan távol van tőlünk, hogy a Föld felszínén egy adott területen nagyjából teljesen mindegy, hogy hol helyezkedik el egy objektum, a nap mindig ugyanolyan irányból és intenzitással világítja meg. Az irányfényforrás irányának természetesen fontos szerepe van, itt a számolás lényegesen bonyolultabb, mint az ambiens fényforrás esetén. Például egy felülről megvilágított szobában az asztal teteje sokkal világosabb, mint az asztal alja. Ahhoz, hogy ezt a hatást el tudjuk érni, kizárólag egyszerű fizikára van szükségünk. Tegyük fel, hogy egy anyagra két azonos erősségű fénysugár esik, az egyik merőlegesen, a másik theta szögben.
* Ha a merőlegesen eső sugár átmérője egységnyi, akkor a theta szögben eső sugár esetében az a felület amin ugyan annyi energia eloszlik sokkal nagyobb. Könnyen levezethető, hogy az egységnyi felületre eső energia (azaz a megvilágítás ereje) cos(theta)-val arányos.
* Ha a merőlegesen eső sugár átmérője egységnyi, akkor a theta szögben eső sugár esetében az a felület amin ugyanannyi energia eloszlik sokkal nagyobb. Könnyen levezethető, hogy az egységnyi felületre eső energia (azaz a megvilágítás ereje) cos(theta)-val arányos.
* A beesési szög kiszámításához szükségünk van a felületi normálra. Még jó, hogy korábban gondoltunk erre. A cos(theta) kiszámításának egy egyszerű módja a skaláris szorzat használata. Ugyanis definíció szerint u * v = |u| * |v| * cos(theta). De ha u-t és v-t úgy választjuk meg, hogy egységnyi hosszúak legyenek, akkor a skaláris szorzat a cos(theta)-t adja. Ha a cos(theta) negatív, akkor a test takarásban van, és az irányfény semmit nem befolyásol a színén.
* A beesési szög kiszámításához szükségünk van a felületi normálra. Még jó, hogy korábban gondoltunk erre. A cos(theta) kiszámításának egy egyszerű módja a skaláris szorzat használata. Ugyanis definíció szerint u * v = |u| * |v| * cos(theta). De ha u-t és v-t úgy választjuk meg, hogy egységnyi hosszúak legyenek, akkor a skaláris szorzat a cos(theta)-t adja. Ha a cos(theta) negatív, akkor a test takarásban van, és az irányfény semmit nem befolyásol a színén.


Ezzel a modellel csak olyan anyagok jeleníthetőek meg jól, amikre egy felületi pont, konstans megvilágítás esetén mindig ugyanúgy néz ki, akárhonnan is nézzük. Ilyen anyag például a legtöbb műanyag, vagy mondjuk egy szivacs. Ezek diffúz anyagok, a fényt minden irányba ugyan olyan intenzitással szórják. Ez a modell kiegészítés nélkül nem működik az olyan anyagokra, amiken a fény megtud csillanni, vagy amikben látjuk a tükörképünket, és azokra se amiken átlátunk.
Ezzel a modellel csak olyan anyagok jeleníthetőek meg jól, amikre egy felületi pont, konstans megvilágítás esetén mindig ugyanúgy néz ki, akárhonnan is nézzük. Ilyen anyag például a legtöbb műanyag, vagy mondjuk egy szivacs. Ezek diffúz anyagok, a fényt minden irányba ugyanolyan intenzitással szórják. Ez a modell kiegészítés nélkül nem működik az olyan anyagokra, amiken a fény megtud csillanni, vagy amikben látjuk a tükörképünket, és azokra se amiken átlátunk.
   
   
Ennek a modellnek egy lehetséges implementációja:
Ennek a modellnek egy lehetséges implementációja:
902. sor: 898. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Az eddigi elmélet összerakva egy programmá: [[Média:Grafpp_raytrace_kocka.cpp‎|Kocka-tracer]]
Az eddigi elmélet összerakva egy programmá: ''<Törölve, túl sokan másolták>''
<br/>
<br/>
Az sugárkövetés eredménye(baloldalt), összehasonlítva azzal, amit az OpenGL tud (glutSolidCube, jobboldalt), hasonló beállítások mellett:  
Az sugárkövetés eredménye(baloldalt), összehasonlítva azzal, amit az OpenGL tud (glutSolidCube, jobboldalt), hasonló beállítások mellett:  
968. sor: 964. sor:
Ezen változtassuk, rakjuk rá a kockát valami talajra, hogy ne lebegjen (így legalább csak a talaj lebeg, nem a kocka), és használjunk valami hihetőbb háttérszínt, mint a teljesen fekete, a megvilágítás szemléltetéséhez.
Ezen változtassuk, rakjuk rá a kockát valami talajra, hogy ne lebegjen (így legalább csak a talaj lebeg, nem a kocka), és használjunk valami hihetőbb háttérszínt, mint a teljesen fekete, a megvilágítás szemléltetéséhez.


Például: [[Média:Grapp_raytrace_egyszeru_kornyezet.cpp|Egyszerű környezet]]
Például: ''<Törölve, túl sokan másolták>''


http://i.imgur.com/16ohvtS.jpg <br/>
http://i.imgur.com/16ohvtS.jpg <br/>
978. sor: 974. sor:
Technikailag mindkét algoritmust fogjuk használni. A második - bár bonyolultabbnak tűnhet - de irányfényforrásokra csak az módszer működik. Az irányfényforrások ugyanis végtelen távol vannak, ahonnan nem tudok elindulni, viszont a felületi pontból a fény irányába el tudunk. A pontfényforrások esetén viszont az első algoritmus egyszerűbb egy picivel.
Technikailag mindkét algoritmust fogjuk használni. A második - bár bonyolultabbnak tűnhet - de irányfényforrásokra csak az módszer működik. Az irányfényforrások ugyanis végtelen távol vannak, ahonnan nem tudok elindulni, viszont a felületi pontból a fény irányába el tudunk. A pontfényforrások esetén viszont az első algoritmus egyszerűbb egy picivel.


Fontos megjegyezni, hogy például irányfényforrások esetén a sugarat nem indíthatjuk pontosan a felületi pontról, hiszen akkor az ahhoz legközelebbi metszéspont maga a felületi pont lenne amiből indítottuk. Ez persze nem mindig teljesülne, a számolási pontosság miatt, ezért néhol árnyékban lesz, néhol nem. Ezt a jelenséget "árnyék pattanás"-nak hívja a szakirodalom (shadow acne). Ez nagyon jellemző mintázatot okoz, ami általában könnyű detektálni. Az alábbi kép mutat egy példát erre:
Fontos megjegyezni, hogy például irányfényforrások esetén a sugarat nem indíthatjuk pontosan a felületi pontról, hiszen akkor az ahhoz legközelebbi metszéspont maga a felületi pont lenne amiből indítottuk. Ez persze nem mindig teljesülne, a számolási pontosság miatt, ezért néhol árnyékban lesz, néhol nem. Ezt a jelenséget "árnyék pattanás"-nak hívja a szakirodalom (shadow acne). Ez nagyon jellemző mintázatot okoz, amit általában könnyű detektálni. Az alábbi kép mutat egy példát erre:


http://i.imgur.com/TeDR81x.png
http://i.imgur.com/TeDR81x.png
1 014. sor: 1 010. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Példaprogram: [[Média:Grafpp_raytrace_arnyekok.cpp|Árnyékok]]
Példaprogram: ''<Törölve, túl sokan másolták>''


<br/>
<br/>
1 038. sor: 1 034. sor:
http://i.imgur.com/ZRgJgHx.png <br/>
http://i.imgur.com/ZRgJgHx.png <br/>


A kép baloldala jól néz ki, van két egészen hihető árnyékunk. Maga a kocka is egész jó. Viszont a képnek szinte a teljes jobb oldala kiégett, és minden ugyan olyan fehér, ordít róla, hogy mű. És ezek csak 2 db 20 W-os izzó...
A kép baloldala jól néz ki, van két egészen hihető árnyékunk. Maga a kocka is egész jó. Viszont a képnek szinte a teljes jobb oldala kiégett, és minden ugyanolyan fehér, ordít róla, hogy mű. És ezek csak 2 db 20 W-os izzó...


Vegyünk két 50 W-os izzót:
Vegyünk két 50 W-os izzót:
1 044. sor: 1 040. sor:
http://i.imgur.com/z4fAqu0.png <br/>
http://i.imgur.com/z4fAqu0.png <br/>


A kocka még mindig jól néz ki. Ellenben itt már árnyékok is kiégtek, és az árnyékok nagy része ugyan olyan fényes, mint az, ami közvetlenül meg van világítva. És ezek még mindig teljesen hétköznapi értékek, két db 50 W-os izzó... Ha ez nem megy, akkor a Nappal mit kezdjünk?
A kocka még mindig jól néz ki. Ellenben itt már árnyékok is kiégtek, és az árnyékok nagy része ugyanolyan fényes, mint az, ami közvetlenül meg van világítva. És ezek még mindig teljesen hétköznapi értékek, két db 50 W-os izzó... Ha ez nem megy, akkor a Nappal mit kezdjünk?


Egy megoldás lehetne, hogy mindent sokkal sötétebbre veszünk, úgy, hogy a Nap fénye legyen a teljesen fehér. Annál világosabbal nem nagyon szoktunk dolgozni. Igen ám, de az emberi szem egy sötét szobában se teljesen vak, ha hihető képet akarunk, akkor egy olyan algoritmus kéne, ami ilyen körülmények között is élvezhető képet ad.
Egy megoldás lehetne, hogy mindent sokkal sötétebbre veszünk, úgy, hogy a Nap fénye legyen a teljesen fehér. Annál világosabbal nem nagyon szoktunk dolgozni. Igen ám, de az emberi szem egy sötét szobában se teljesen vak, ha hihető képet akarunk, akkor egy olyan algoritmus kéne, ami ilyen körülmények között is élvezhető képet ad.
1 098. sor: 1 094. sor:


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
   x = max(0, InputColor-0.004);
   x = max(0, InputLuminance-0.004);
   OutputColor = (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);
   OutputLuminance = (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


1 127. sor: 1 123. sor:
=== Spekulráis anyagok ===
=== Spekulráis anyagok ===


Az anyagok amikkel eddig dolgoztunk teljesen diffúzak voltak. A teljesen diffúz anyag egy pontja, konstans megvilágítás mellett mindig ugyan úgy néz ki, akárhonnan, akármilyen szögből is nézzük. A valós anyagok viszont nem mind így viselkednek.
Az anyagok amikkel eddig dolgoztunk teljesen diffúzak voltak. A teljesen diffúz anyag egy pontja, konstans megvilágítás mellett mindig ugyanúgy néz ki, akárhonnan, akármilyen szögből is nézzük. A valós anyagok viszont nem mind így viselkednek.


Most azzal fogunk foglalkozni, hogy bizonyos anyagokon egy lámpa fénye meg tud csillanni, ha megfelelő irányból nézzük. Ilyen pl. egy lakkozott fa felület. Fontos megjegyezni, hogy ez nem úgy viselkedik mint egy tükör, nem látjuk rajta a tükörképünket, csak egy megcsillanó foltot.
Most azzal fogunk foglalkozni, hogy bizonyos anyagokon egy lámpa fénye meg tud csillanni, ha megfelelő irányból nézzük. Ilyen pl. egy lakkozott fa felület. Fontos megjegyezni, hogy ez nem úgy viselkedik mint egy tükör, nem látjuk rajta a tükörképünket, csak egy megcsillanó foltot.
1 154. sor: 1 150. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Itt se feledkezzünk el az árnyékokról. Szerencsére az árnyékszámítás itt is teljesen ugyan az, ezért célszerű a láthatóságot egy külön függvényben eldönteni.
Itt se feledkezzünk el az árnyékokról. Szerencsére az árnyékszámítás itt is teljesen ugyanaz, ezért célszerű a láthatóságot egy külön függvényben eldönteni.


A 'shininess' meghatározása teljesen mértékben hasra-ütésre, próbálgatással szokott menni. Én személy szerint a kettőhatvány shinnines értékekkel szoktam először próbálkozni (8 - 16 - 32 - 64 a leggyakoribb nálam), és utána esetleg "finom hangolom" az értéket, az alapján, hogy melyik éréték néz ki jól.
A 'shininess' meghatározása teljesen mértékben hasra-ütésre, próbálgatással szokott menni. Én személy szerint a kettőhatvány shinnines értékekkel szoktam először próbálkozni (8 - 16 - 32 - 64 a leggyakoribb nálam), és utána esetleg "finom hangolom" az értéket, az alapján, hogy melyik éréték néz ki jól.
1 160. sor: 1 156. sor:
A spekuláris megcsillanások nagyon sokat tudnak dobni egy kép hihetőségén.
A spekuláris megcsillanások nagyon sokat tudnak dobni egy kép hihetőségén.


Például: [[Média:Grafpp_raytrace_specular_highlights.cpp‎|Spekuláris megcsillanás egy kockán]]
Például: ''<Törölve, túl sokan másolták>''


http://i.imgur.com/uCHgf9g.jpg
http://i.imgur.com/uCHgf9g.jpg
1 201. sor: 1 197. sor:
http://i.imgur.com/GLBoT7h.jpg
http://i.imgur.com/GLBoT7h.jpg


Ezzel akad egy érdekes probléma, ami nekem egy héten át fel se tűnt. A költői kérdés a következő: Ha tökéletes a tükrünk, és pont ugyan azt látjuk benne, mint ami felette is van, akkor miért különöl el ilyen egyértelműen a háttérszíntől? A válasz persze az, hogy 5 példaprogrammal ezelőtt hoztam egy olyan döntést, hogy a tonemap-et a háttérszínre nem alkalmazom. Ez akkor még jó ötletnek tűnt, de a tükröző anyagok bevezetésével teljesen fals eredményt okoz. Ha a tonemapet a teljes képernyőre alkalmazom, akkor ezt az eredményt kapom:
Ezzel akad egy érdekes probléma, ami nekem egy héten át fel se tűnt. A költői kérdés a következő: Ha tökéletes a tükrünk, és pont ugyanazt látjuk benne, mint ami felette is van, akkor miért különöl el ilyen egyértelműen a háttérszíntől? A válasz persze az, hogy 5 példaprogrammal ezelőtt hoztam egy olyan döntést, hogy a tonemap-et a háttérszínre nem alkalmazom. Ez akkor még jó ötletnek tűnt, de a tükröző anyagok bevezetésével teljesen fals eredményt okoz. Ha a tonemapet a teljes képernyőre alkalmazom, akkor ezt az eredményt kapom:


http://i.imgur.com/M09ZnHA.jpg
http://i.imgur.com/M09ZnHA.jpg
1 222. sor: 1 218. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Példaprogram: [[Média:Grafpp_raytrace_tukrok.cpp‎|Két szemben lévő tükör]]
Példaprogram: ''<Törölve, túl sokan másolták>''


http://i.imgur.com/jwtHLsb.jpg
http://i.imgur.com/jwtHLsb.jpg
1 270. sor: 1 266. sor:
Az 'n' és 'k' paraméterek az anyagra jellemzőek, általában a házi kiírásban meg vannak adva. Például ezüst esetén n = (0.14, 0.16, 0.13), k = (4.1, 2.3, 3.1)
Az 'n' és 'k' paraméterek az anyagra jellemzőek, általában a házi kiírásban meg vannak adva. Például ezüst esetén n = (0.14, 0.16, 0.13), k = (4.1, 2.3, 3.1)


Példaprogram: [[Média:Grafpp_raytrace_ezust.cpp‎|Ezüst]]
Példaprogram: ''<Törölve, túl sokan másolták>''


http://i.imgur.com/vJKsR53.jpg
http://i.imgur.com/vJKsR53.jpg
1 311. sor: 1 307. sor:
A Fresnelhez használandó beesési szög, az jelen esetben a félszög vektor és a nézeti vektor között értendő. Azért ez a két vektor kell nekünk, mert a visszaverődésben résztvevő mikro-tükröknek a félszög vektor a felületi normálja, és a nézeti irányba verődnek vissza. Tehát egyszerűen a spekuláris megcsillanás színét meg kell szoroznunk a <code> F(max(dot(V, H), 0.0f)) </code> vektorral.
A Fresnelhez használandó beesési szög, az jelen esetben a félszög vektor és a nézeti vektor között értendő. Azért ez a két vektor kell nekünk, mert a visszaverődésben résztvevő mikro-tükröknek a félszög vektor a felületi normálja, és a nézeti irányba verődnek vissza. Tehát egyszerűen a spekuláris megcsillanás színét meg kell szoroznunk a <code> F(max(dot(V, H), 0.0f)) </code> vektorral.


Példaprogram: [[Média:Grafpp_raytrace_fresnel_specular.cpp‎| Fresnel spekuláris megvilágítás]]
Példaprogram: ''<Törölve, túl sokan másolták>''


Ha az F0-ra az üvegre jellemző 0.04f-re választjuk meg, az ilyen hatást eredményez:
Ha az F0-ra az üvegre jellemző 0.04f-re választjuk meg, az ilyen hatást eredményez:


(Megjegyzés: a kocka spekuláris színét a két esetben úgy választottam meg, hogy nagyjából azonos legyen a spekuláris megcsillanás fényessége az első képen, de nyilván a Fresnel tag miatt ugyan az a spekuláris szín mellet az sokkal sötétebb volt)  
(Megjegyzés: a kocka spekuláris színét a két esetben úgy választottam meg, hogy nagyjából azonos legyen a spekuláris megcsillanás fényessége az első képen, de nyilván a Fresnel tag miatt ugyanaz a spekuláris szín mellet az sokkal sötétebb volt)  


Fresnel (bal oldalt) összehasonlítása a Blinn-Phong-gal (jobb oldalt).
Fresnel (bal oldalt) összehasonlítása a Blinn-Phong-gal (jobb oldalt).
1 336. sor: 1 332. sor:
A spekulráis anyagokkal ellentétben a tükröző anyagokról alkotott modellünk teljesen figyelmen kívül hagyta az elsődleges fényforrásokat, színüket csak a környezetben lévő objektumokról visszaverődő fény befolyásolta. Pedig a valóságban nem az a tapasztalat, hogy ha egy tükrön keresztül nézünk a Napba, akkor nem látjuk azt.
A spekulráis anyagokkal ellentétben a tükröző anyagokról alkotott modellünk teljesen figyelmen kívül hagyta az elsődleges fényforrásokat, színüket csak a környezetben lévő objektumokról visszaverődő fény befolyásolta. Pedig a valóságban nem az a tapasztalat, hogy ha egy tükrön keresztül nézünk a Napba, akkor nem látjuk azt.


Az elsődleges fényforrások visszaverődésével a legfőbb problémánk az, hogy például egy pontfényforrás esetében a fényforrás tükörképe továbbra is pontszerű, ami olyan kicsi, hogy azt nem látjuk. Az irányfényforrásokkal is ugyan ez az eset, csak egy végtelenül kicsi térszög alól látszódnak.  
Az elsődleges fényforrások visszaverődésével a legfőbb problémánk az, hogy például egy pontfényforrás esetében a fényforrás tükörképe továbbra is pontszerű, ami olyan kicsi, hogy azt nem látjuk. Az irányfényforrásokkal is ugyanez az eset, csak egy végtelenül kicsi térszög alól látszódnak.  


Technikailag a modellnek két részlete okozza ezt az anomáliát: ideális tükröt és ideális fényforrásokat feltételezünk egyszerre. A kettő egyszerre nem az igazi, ezért az egyikről le kell mondanunk. Az ideális tükörről sokkal könnyebb lemondani, ezért én azt választom.  
Technikailag a modellnek két részlete okozza ezt az anomáliát: ideális tükröt és ideális fényforrásokat feltételezünk egyszerre. A kettő egyszerre nem az igazi, ezért az egyikről le kell mondanunk. Az ideális tükörről sokkal könnyebb lemondani, ezért én azt választom.  
1 346. sor: 1 342. sor:
Egy kis trükkre azonban szükségünk van. a pontfényforrások távolságfüggéséhez ugyanis követnünk kell, hogy a sugár összesen mennyi utat tett meg eddig.
Egy kis trükkre azonban szükségünk van. a pontfényforrások távolságfüggéséhez ugyanis követnünk kell, hogy a sugár összesen mennyi utat tett meg eddig.


Példaprogram: [[Média:Grafpp_raytrace_spekularis_tukor.cpp‎|Spekuláris tükör]]
Példaprogram: ''<Törölve, túl sokan másolták>''


A spekuláris tükör fizikailag nem túl korrekt, de jól néz ki. Például ilyen hatást lehet vele elérni:
A spekuláris tükör fizikailag nem túl korrekt, de jól néz ki. Például ilyen hatást lehet vele elérni:
1 364. sor: 1 360. sor:
De irányfényforrásokra sokkal hihetőbb hatást ér el az ötlet.
De irányfényforrásokra sokkal hihetőbb hatást ér el az ötlet.


Például: [[Média:Grafpp_raytrace_lathato_fenyforras.cpp‎|Látható Nap]]
Például: ''<Törölve, túl sokan másolták>''


http://i.imgur.com/BxeEztT.jpg
http://i.imgur.com/BxeEztT.jpg
1 399. sor: 1 395. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Fontos még, hogy a Fresnel egyenletben kifele menet is ugyan azt a törésmutatót kell használni. Az F0 kiszámolásakor az egyesek a levegő törésmutatója helyén állnak, általános esetben a képlet a <code>((n1 - n2)^2 + k*k) / ((n1 + n2)^2  + k*k)</code> alakot veszi fel. Ez a képlet viszont n1 és n2 szempontjából szimmetrikus, így '''nem kell két F0-t számolni''', egyet a befele, egyet meg a kifele menő sugarakhoz.
Fontos még, hogy a Fresnel egyenletben kifele menet is ugyanazt a törésmutatót kell használni. Az F0 kiszámolásakor az egyesek a levegő törésmutatója helyén állnak, általános esetben a képlet a <code>((n1 - n2)^2 + k*k) / ((n1 + n2)^2  + k*k)</code> alakot veszi fel. Ez a képlet viszont n1 és n2 szempontjából szimmetrikus, így '''nem kell két F0-t számolni''', egyet a befele, egyet meg a kifele menő sugarakhoz.


Csak olyan törő anyagokkal foglalkozunk, amiknek a törésmutatója a hullámhossztól független. A nem így viselkedő anyagok, a prizmák nagyon látványos képeket tudnak eredményezni, de sokkal számításigényesebbek és bonyolultabbak, ezért most nem fogok velük foglalkozni.
Csak olyan törő anyagokkal foglalkozunk, amiknek a törésmutatója a hullámhossztól független. A nem így viselkedő anyagok, a prizmák nagyon látványos képeket tudnak eredményezni, de sokkal számításigényesebbek és bonyolultabbak, ezért most nem fogok velük foglalkozni.
1 444. sor: 1 440. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Példaprogram: [[Média:Grafpp_raytrace_uveg.cpp|Üvegkocka]]
Példaprogram: ''<Törölve, túl sokan másolták>''
{|
{|
|-
|-
1 473. sor: 1 469. sor:


A "foton" igazándiból foton csomagot jelent, pl. a törő anyagok kétfelé választhatnak egy ilyen csomagot.
A "foton" igazándiból foton csomagot jelent, pl. a törő anyagok kétfelé választhatnak egy ilyen csomagot.
A foton tudja magáról, hogy milyen színű. A kölcsönhatásokkor a Fresnel egyenlet következtében a foton színe megváltozhat. Az egyes fotonok energiája legyen fordítottan arányos a fotonok számával. A fényforrás energiája az összes fotonra egyenletesen oszlik szét. Ha megkétszerezzük a fotonok számát, akkor is ugyan olyan fényes jelentet akarunk kapni.
A foton tudja magáról, hogy milyen színű. A kölcsönhatásokkor a Fresnel egyenlet következtében a foton színe megváltozhat. Az egyes fotonok energiája legyen fordítottan arányos a fotonok számával. A fényforrás energiája az összes fotonra egyenletesen oszlik szét. Ha megkétszerezzük a fotonok számát, akkor is ugyanolyan fényes jelentet akarunk kapni.


A törő és tükröző anyagok pont ugyan úgy lépnek kölcsönhatásba a fotonokkal, mint ahogy a sugarakkal is. Ennek az implementáláshoz semmi új ötlet nem kell.  
A törő és tükröző anyagok pont ugyanúgy lépnek kölcsönhatásba a fotonokkal, mint ahogy a sugarakkal is. Ennek az implementáláshoz semmi új ötlet nem kell.  


Például egy felülről megvilágított üvegkocka így szórja a fényt: [[Média:Grafpp_raytrace_globalis_illum.cpp‎|Globális illumináció]]
Például egy felülről megvilágított üvegkocka így szórja a fényt: ''<Törölve, túl sokan másolták>''


{|
{|
1 486. sor: 1 482. 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 negyedik és az ötödik házihoz 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 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 498. sor: 1 494. sor:
* Az árnyékok széle recésebb lesz. A bilineáris szűrés miatt a globális illumináció pontosabban határozza meg az árnyékok szélét, mint az a módszer, amit a lokális illuminációnál használtunk.
* Az árnyékok széle recésebb lesz. A bilineáris szűrés miatt a globális illumináció pontosabban határozza meg az árnyékok szélét, mint az a módszer, amit a lokális illuminációnál használtunk.


Példaprogram: [[Média:Grafpp_raytrace_ketiranyu_sugarkovetes.cpp‎|Kétirányú sugárkövetés]]
Példaprogram: ''<Törölve, túl sokan másolták>''


A korábbi jelenet, csak kétirányú (bal oldalt) és baloldalt (jobb oldalt) sugárkövetéssel, mindkét esetben 500 000 fotonnal
A korábbi jelenet kétirányú (bal oldalt) és csak globális (jobb oldalt) megvilágítással, mindkét esetben 500 000 fotonnal
{|
{|
|-
|-
1 559. sor: 1 555. sor:
<br/>
<br/>


== A negyedik és az ötödik házikhoz szükséges elmélet ==
== 3D OpenGL ==


=== Kedvcsináló ===
=== Kedvcsináló ===


Sugárkövetéssel nagyon látványos képeket tudunk elérni, ha vesszük a fáradtságot, hogy érdekes objektumokat helyezzünk el a világban. Főleg a másodrendű felületek tudnak nagyon szép képeket adni. Az árnyékszámításhoz gyakorlatilag ölünkbe hullott egy algoritmus, amihez pont ugyan azt kellett csinálni, mint amit rajzoláskor is csináltunk. A globális illuminációval ráadásul olyan hatásokat is implementálni tudtunk, amikkel a mai játékokba szinte sehol nem lehet találkozni. Akkor mi a gond a sugárkövetéssel, miért nem ezt használjuk mindenhol, miért kell akkor egyáltalán OpenGL? A probléma az vele, hogy lassú. Az általam mutatott programok mindössze 14 darab háromszögből álltak, messze elmaradva a mai játékok komplexitásától, és így is, a globális illuminációval kb. 5 másodperc volt, mire a kép előállt. Ez nem tűnik soknak, de optimális esetben egy játékhoz másodpercenként legalább 60szor kéne képet alkotnunk, a sugárkövető ettől messze elmarad. Ha a kódot két héten át optimalizáltam volna, akkor akár 0.1 másodperc is lehetne a renderelési idő. Igen ám, de ez csak a képalkotásra szánt idő! A játék logika, főleg ütközés detektálások, vagy ruha-, víz szimuláció stb. egyáltalán nincsenek ingyen, és még azoknak is bele kéne férnie az időbe. És ez még mindig csak 14 darab háromszög.
Sugárkövetéssel nagyon látványos képeket tudunk elérni, ha vesszük a fáradtságot, hogy érdekes objektumokat helyezzünk el a világban. Főleg a másodrendű felületek tudnak nagyon szép képeket adni. Az árnyékszámításhoz gyakorlatilag ölünkbe hullott egy algoritmus, amihez pont ugyanazt kellett csinálni, mint amit rajzoláskor is csináltunk. A globális illuminációval ráadásul olyan hatásokat is implementálni tudtunk, amikkel a mai játékokba szinte sehol nem lehet találkozni. Akkor mi a gond a sugárkövetéssel, miért nem ezt használjuk mindenhol, miért kell akkor egyáltalán OpenGL? A probléma az vele, hogy lassú. Az általam mutatott programok mindössze 14 darab háromszögből álltak, messze elmaradva a mai játékok komplexitásától, és így is, a globális illuminációval kb. 5 másodperc volt, mire a kép előállt. Ez nem tűnik soknak, de optimális esetben egy játékhoz másodpercenként legalább 60szor kéne képet alkotnunk, a sugárkövető ettől messze elmarad. Ha a kódot két héten át optimalizáltam volna, akkor akár 0.1 másodperc is lehetne a renderelési idő. Igen ám, de ez csak a képalkotásra szánt idő! A játék logika, főleg ütközés detektálások, vagy ruha-, víz szimuláció stb. egyáltalán nincsenek ingyen, és még azoknak is bele kéne férnie az időbe. És ez még mindig csak 14 darab háromszög.
 
A általunk használt sugárkövetéssel a legnagyobb gond az, hogy nem használja a videókártyát. Hiába van a gépünkbe egy szuperszámítógép teljesítményű hardware, ha nem használjuk semmire. A mai videókártyák nem csak előre meghatározott műveletek tudnak végrehajtani, hanem már programozhatóak is. Az ezt kihasználó grafikus programok hihetetlen hatékonyak. Például az én grafika nagyházim egy 72 millió háromszögből álló jelenetet tudott valós időben (kb. 20 FPS-el) kirajzolni (fizikával együtt).


A általunk használt sugárkövetéssel a legnagyobb gond az, hogy nem használja a videókártyát. Hiába van a gépünkbe egy szuperszámítógép teljesítményű hardware, ha nem használjuk semmire. A mai videókártyák nem csak előre meghatározott műveletek tudnak végrehajtani, hanem már programozhatóak is. Az ezt kihasználó grafikus programok hihetetlen hatékonyak. Például az én grafika nagyházim egy 72 millió háromszögból álló jelenetet tudott valós időben (kb. 25 FPS-el) kirajzolni.


<div style="text-align:center;margin:0px auto;">
<div style="text-align:center;margin:0px auto;">
http://i.imgur.com/fGQv8Wp.png
http://i.imgur.com/zYUuZ0L.png
</div>
</div>


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ögből álló jelenet real-time kirajzolása még messze nem a maximum amit el lehet érni.


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 az első meg a második háziban, csak 3D-ben).
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 második házinál mutattam egy glut függvényt, ami egy kockát rajzol ki. Ezt a házikhoz nem lehet használni, inkább írjunk egyet magunknak.  
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 ez 1.
* 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 ugyan azt 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 ugyan arra a (0..1) intervallumra transzformálni. Ez pedig azzal jár hogy az egyre távolabb lévő pontokhoz is ugyan azt 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.


Például:
Például:
1 817. sor: 1 814. sor:
http://i.imgur.com/iZJlFr9.gif
http://i.imgur.com/iZJlFr9.gif


* A megvilágítást közvetlenül a setCamera() függvény után állítjuk be. Ekkora a glLightfv-ben megadott pozíció a világ koordináta-rendszerben lesz értelmezve, és a statikus objektumoknak "mindig ugyan az oldala lesz fényes". Ezt általában akkor szoktuk használni, ha a fényforrás a jelentben egy helyben marad.  
* A megvilágítást közvetlenül a setCamera() függvény után állítjuk be. Ekkora a glLightfv-ben megadott pozíció a világ koordináta-rendszerben lesz értelmezve, és a statikus objektumoknak "mindig ugyanaz az oldala lesz fényes". Ezt általában akkor szoktuk használni, ha a fényforrás a jelentben egy helyben marad.  


http://i.imgur.com/cUPVzeT.gif
http://i.imgur.com/cUPVzeT.gif
1 837. sor: 1 834. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


Ugyan azt látjuk, mint a három sor nélkül, de fele annyi erőfeszítéssel.
Ugyanazt látjuk, mint a három sor nélkül, de fele annyi erőfeszítéssel.


http://i.imgur.com/dzjcMuZ.png  
http://i.imgur.com/dzjcMuZ.png  
1 902. sor: 1 899. sor:


* Az UV sphere esetében a sarkoknál közel nulla területűek a négyszögek, ott teljesen fölöslegesen kell számolnia az OpenGL-nek. Az icosphere esetében viszont a polygonok egyenletesen vannak elhelyezve, és ugyanakkora területűek. Az icosphere tehát jobb tesszelációja a gömbnek, mint az UV sphere. De az UV sphere-t megírni sokkal egyszerűbb, és a házikhoz teljesen elegendő.
* Az UV sphere esetében a sarkoknál közel nulla területűek a négyszögek, ott teljesen fölöslegesen kell számolnia az OpenGL-nek. Az icosphere esetében viszont a polygonok egyenletesen vannak elhelyezve, és ugyanakkora területűek. Az icosphere tehát jobb tesszelációja a gömbnek, mint az UV sphere. De az UV sphere-t megírni sokkal egyszerűbb, és a házikhoz teljesen elegendő.
* Két tesszelláció között nyilván különbség lehet, hogy mennyire jellegzetes pontokat ragad meg az eredeti objektumból. Például vegyünk egy síkot, amibe vannak keskeny kiugróan magas pontok. A tesszellációba a kiugró pontok elhagyása az alakzat összhatását teljesen el tudja rontani, míg ha ezeket is ugyan olyan súllyal vesszük be, mint a többi sík pontot, akkor összességébe az alakzatunk dimbes-dombos lesz, mind a sík volta, mind a kiugró pontok jellegzetessége elveszik.
* Két tesszelláció között nyilván különbség lehet, hogy mennyire jellegzetes pontokat ragad meg az eredeti objektumból. Például vegyünk egy síkot, amibe vannak keskeny kiugróan magas pontok. A tesszellációba a kiugró pontok elhagyása az alakzat összhatását teljesen el tudja rontani, míg ha ezeket is ugyanolyan súllyal vesszük be, mint a többi sík pontot, akkor összességébe az alakzatunk dimbes-dombos lesz, mind a sík volta, mind a kiugró pontok jellegzetessége elveszik.
* Két tesszelláció között nagyon fontos különbséget okozhatnak az árnyaló normálok. De mielőtt ebbe beleszaladnánk, nézzük meg, hogy mik is azok.
* Két tesszelláció között nagyon fontos különbséget okozhatnak az árnyaló normálok. De mielőtt ebbe beleszaladnánk, nézzük meg, hogy mik is azok.


Megjegyzés: Általában parametrikus felületeket kell tesszellálni. Itt az összes pontnak a felsorolása - az alakzattól függetlenül - két darab for loop. Az objektum kirajzolása ettől mindössze annyiban különbözik, hogy a szomszédos pontokból primitíveket is kell alkotni. De szerencsére általában a szomszédos pontok paraméterei is szomszédosak. Bár elsőre meglepőnek hangozhat, de egy gömb és egy tórusz tesszellációja csak annyiban különbözik, hogy a két futó paraméterhez hogyan rendeljük hozzá a 3D-s pontot, vagyis a két alakzatnak csak az egyenlete más, ezt leszámítva a két objektum kirajzolásának a kódja teljesen ugyanaz. Sőt, amikor a kockának az egyes oldalait, a négyzeteket bontottam fel nagyobb részletességűre a megvilágításhoz, még ott is ugyan az az algoritmus került elő, mint ami a gömbnél vagy a tórusznál kell, egyedül az alakzat paraméteres egyenlete volt más.
Megjegyzés: Általában parametrikus felületeket kell tesszellálni. Itt az összes pontnak a felsorolása - az alakzattól függetlenül - két darab for loop. Az objektum kirajzolása ettől mindössze annyiban különbözik, hogy a szomszédos pontokból primitíveket is kell alkotni. De szerencsére általában a szomszédos pontok paraméterei is szomszédosak. Bár elsőre meglepőnek hangozhat, de egy gömb és egy tórusz tesszellációja csak annyiban különbözik, hogy a két futó paraméterhez hogyan rendeljük hozzá a 3D-s pontot, vagyis a két alakzatnak csak az egyenlete más, ezt leszámítva a két objektum kirajzolásának a kódja teljesen ugyanaz. Sőt, amikor a kockának az egyes oldalait, a négyzeteket bontottam fel nagyobb részletességűre a megvilágításhoz, még ott is ugyanaz az algoritmus került elő, mint ami a gömbnél vagy a tórusznál kell, egyedül az alakzat paraméteres egyenlete volt más.


http://i.imgur.com/KCJ9s0Q.jpg
http://i.imgur.com/KCJ9s0Q.jpg
1 931. sor: 1 928. sor:
http://i.imgur.com/yy8eRvZ.png
http://i.imgur.com/yy8eRvZ.png


A világítás számításakor csak a normál változott meg, és nem nehéz kitalálni, hogy fele olyan hosszú lett. De nem kétszeresére nagyítottuk a világot? Akkor a normál miért lett fele akkora. Ha ugyan az maradt volna a normál, akkor működne jól. De akkor az OpenGL miért rontotta el?
A világítás számításakor csak a normál változott meg, és nem nehéz kitalálni, hogy fele olyan hosszú lett. De nem kétszeresére nagyítottuk a világot? Akkor a normál miért lett fele akkora. Ha ugyanaz maradt volna a normál, akkor működne jól. De akkor az OpenGL miért rontotta el?


Ahhoz, hogy ezt megértsük, vegyünk nem uniform nagyítást, ami a tengelyek irányába különböző mértékben nagyít. Pl. nagyítsuk a (3, 1, 1) vektorral. Ez nyilván drasztikusabb alakváltozással, és így a normálok lényegesebb megváltozásával is jár. Ennek az eredménye egy ellipszis lesz:
Ahhoz, hogy ezt megértsük, vegyünk nem uniform nagyítást, ami a tengelyek irányába különböző mértékben nagyít. Pl. nagyítsuk a (3, 1, 1) vektorral. Ez nyilván drasztikusabb alakváltozással, és így a normálok lényegesebb megváltozásával is jár. Ennek az eredménye egy ellipszis lesz:
1 937. sor: 1 934. sor:
http://i.imgur.com/RG9NlDC.png
http://i.imgur.com/RG9NlDC.png


Az ellipszisen egyrészt nagyon jól látszódik, hogy az a tesszellációs felbontás, ami a gömbhöz elég volt, itt már csúnya képet eredményez. De most nem ezen van a lényeg, hanem az árnyaló normálokon. Miben másak az ellipszis normáljai, mint a gömbé? Az X tengely mentén ugyan annyi változás 3-szor akkora távon következik be, ezért értelemszerűen, amíg a Y és a Z tengely mentén a egységnyit változik a felületi pont helye, addig az X tengely mentén ez az érték 1/3. Termesztésen ez a pongyola megfogalmazás a parciális deriváltak számszerű értékére vonatkozott. És a gradiens, a parc. deriváltakból álló vektor, a felületi normál amit mi kerestünk. Tehát a normálon a (3, 1, 1) nagyítás hatására (1/3, 1, 1) vektorral való skálázás történik. Általánosságban az igaz, hogy a normálokra a nagyítások inverz transzformációja hat.
Az ellipszisen egyrészt nagyon jól látszódik, hogy az a tesszellációs felbontás, ami a gömbhöz elég volt, itt már csúnya képet eredményez. De most nem ezen van a lényeg, hanem az árnyaló normálokon. Miben másak az ellipszis normáljai, mint a gömbé? Az X tengely mentén ugyanannyi változás 3-szor akkora távon következik be, ezért értelemszerűen, amíg a Y és a Z tengely mentén a egységnyit változik a felületi pont helye, addig az X tengely mentén ez az érték 1/3. Termesztésen ez a pongyola megfogalmazás a parciális deriváltak számszerű értékére vonatkozott. És a gradiens, a parc. deriváltakból álló vektor, a felületi normál amit mi kerestünk. Tehát a normálon a (3, 1, 1) nagyítás hatására (1/3, 1, 1) vektorral való skálázás történik. Általánosságban az igaz, hogy a normálokra a nagyítások inverz transzformációja hat.


Szerencsére ezt az OpenGL automatikusan megcsinálja helyettünk. A gond viszont ezzel az, hogy az ilyen transzformációk után a normál már nem feltétlen lesz egységvektor, mint ahogy azt a korábbi példákba is láttuk. De erre a megoldás egyszerű, a <code>glEnable(GL_NORMALIZE);</code> függvény megkéri az OpenGL-t, hogy a világítás számolásakor normalizálja a normálokat.
Szerencsére ezt az OpenGL automatikusan megcsinálja helyettünk. A gond viszont ezzel az, hogy az ilyen transzformációk után a normál már nem feltétlen lesz egységvektor, mint ahogy azt a korábbi példákba is láttuk. De erre a megoldás egyszerű, a <code>glEnable(GL_NORMALIZE);</code> függvény megkéri az OpenGL-t, hogy a világítás számolásakor normalizálja a normálokat.
1 956. sor: 1 953. sor:
|}
|}


Először is, mi is az a textúra? A textúra egy színeket tartalmazó tömb. OpenGL 1.1-be egy vagy két dimenziós lehet, és szabvány szerint kettőhatvány méretűnek kell lennie. Annak ellenére, hogy a textúra színeket tartalmaz, és a színeket az OpenGL floatként szereti kezelni, a textúrák esetében nem annyira szeretünk floatokat használni. Itt tényleg csak (0-1) tarmotány beli LDR színekre vagyunk kíváncsiak, itt float helyett elég egy fix pontos szám is, pl komponensenként egy byte. De gyakran mindhárom komponenst le tudjuk írni mindössze egy bájtban. A float textúrák sokkal több helyet foglalnak, külön megizzasztják a memóriát, ami már enélkül is szűk keresztmetszet, ráadásul ezt nagyjából feleslegesen teszik, a float értékkészletének nagy részét nem is használják ki.  
Először is, mi is az a textúra? A textúra egy színeket tartalmazó tömb. OpenGL 1.1-be egy vagy két dimenziós lehet, és szabvány szerint kettőhatvány méretűnek kell lennie. Annak ellenére, hogy a textúra színeket tartalmaz, és a színeket az OpenGL floatként szereti kezelni, a textúrák esetében nem annyira szeretünk floatokat használni. Itt tényleg csak (0-1) tartománybeli LDR színekre vagyunk kíváncsiak, itt float helyett elég egy fix pontos szám is, pl. komponensenként egy byte. De gyakran mindhárom komponenst le tudjuk írni mindössze egy bájtban. A float textúrák sokkal több helyet foglalnak, külön megizzasztják a memóriát, ami már enélkül is szűk keresztmetszet, ráadásul ezt nagyjából feleslegesen teszik, a float értékkészletének nagy részét nem is használják ki.  


De hogyan állítsuk elő a számokat? Vegyünk egy unsigned char tömböt és kézzel írjuk be az összes pixelre, hogy milyen színű? Majdnem, de ez ilyen formába használhatatlan lenne. Semmi vizuális visszacsatolásunk se lenne, hogy a textúra hogy néz ki. Én ehelyett egy karakter tömbbe (magyarul stringbe) fogok ascii-art számokat rajzolni. Például a '.' karakter jelentsen fehér színt, a '*' feketét, a '+' meg szürkét. Én 8*8-as textúrátkat fogok csinálni minden egyes számnak. A számokat én így álmodtam meg (ér ezeknél szebbeket csinálni):  
De hogyan állítsuk elő a számokat? Vegyünk egy unsigned char tömböt és kézzel írjuk be az összes pixelre, hogy milyen színű? Majdnem, de ez ilyen formába használhatatlan lenne. Semmi vizuális visszacsatolásunk se lenne, hogy a textúra hogy néz ki. Én ehelyett egy karakter tömbbe (magyarul stringbe) fogok ascii-art számokat rajzolni. Például a '.' karakter jelentsen fehér színt, a '*' feketét, a '+' meg szürkét. Én 8*8-as textúrákat fogok csinálni minden egyes számnak. A számokat én így álmodtam meg (ér ezeknél szebbeket csinálni):  


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
2 150. sor: 2 147. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


A hét köztudottan nem kettőhatvány. Ha megpróbáljuk ugyan úgy használni, mint ahogy a 8x8-as textúrával tettük, vajon működni fog?  
A hét köztudottan nem kettőhatvány. Ha megpróbáljuk ugyanúgy használni, mint ahogy a 8x8-as textúrával tettük, vajon működni fog?  


Természetesen nem...
Természetesen nem...
2 163. sor: 2 160. sor:
* RGB textúra helyett használjunk RGBA textúrát.
* RGB textúra helyett használjunk RGBA textúrát.


De várjunk csak, nem arról volt szó, hogy az OpenGL csak kettőhatvány méretű textúrákkal tud dolgozni? De.. ez szabvány szerint igaz... csak a mi gépünk nem tud róla... A 90-es években még problémát okozott a videókártyáknak a nem kettőhatvány méretű textúrák kezelése, de már több mint tíz éve tetszőleges méretű textúrával is ugyan olyan hatékonyan tud dolgozni az összes videókártya. Ez viszont sovány vigasz a beadón... ahol a nem kettőhatvány méretű textúrák nincsenek implementálva.
De várjunk csak, nem arról volt szó, hogy az OpenGL csak kettőhatvány méretű textúrákkal tud dolgozni? De.. ez szabvány szerint igaz... csak a mi gépünk nem tud róla... A 90-es években még problémát okozott a videókártyáknak a nem kettőhatvány méretű textúrák kezelése, de már több mint tíz éve tetszőleges méretű textúrával is ugyanolyan hatékonyan tud dolgozni az összes videókártya. Ez viszont sovány vigasz a beadón... ahol a nem kettőhatvány méretű textúrák nincsenek implementálva.


A beadón működő megoldás:
A beadón működő megoldás:
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, ez még a negyedik házinál sokat segíthet a debuggoláshoz.
É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).


A billentyűlenyomásokat nem kezelhetjük egyszerűen az onKeyboard-ban, ez akkor generál eseményeket, amikor egy karaktert begépelnénk, ami pl megtörténik először a billentyű lenyomásakor, aztán jelentős ideig (kb. 0.3 - 0.5 sec) nem generálódik újabb esemény, majd után másodpercenként kb. 10-20 karakterbeütés-t generál. És ez nekünk nagyon jó, helyette inkább tároljuk el, hogy az egyes billentyűk mikor vannak lenyomott állapotban.
A billentyűlenyomásokat nem kezelhetjük egyszerűen az onKeyboard-ban, ez akkor generál eseményeket, amikor egy karaktert begépelnénk, ami pl megtörténik először a billentyű lenyomásakor, aztán jelentős ideig (kb. 0.3 - 0.5 sec) nem generálódik újabb esemény, majd után másodpercenként kb. 10-20 karakterbeütés-t generál. Ez pl egy karakter irányításához teljesen használhatatlan, helyette inkább tároljuk el, hogy az egyes billentyűk lenyomott állapotban vannak-e.


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
2 321. sor: 2 318. sor:
Az én implementációim: [[Média:Grafpp4_kamera.cpp‎|Kamera]]  
Az én implementációim: [[Média:Grafpp4_kamera.cpp‎|Kamera]]  


http://s28.postimg.org/e5ve8ma63/anim.gif
[[File:Graftutorial_kamera_anim.gif]]


== Utóhang ==
== Utóhang ==
2 328. sor: 2 325. sor:


-----
-----
[https://wiki.sch.bme.hu/Szerkeszt%C5%91:Rohamcsiga RohamCsiga] - 2014.01.
[https://wiki.sch.bme.hu/Szerkeszt%C5%91:Rohamcsiga Csala Tamás] - 2014.01.




[[Category:Infoalap]]
[[Category:Infoalap]]