„Számítógépes grafika házi feladat tutorial” változatai közötti eltérés
a Kijavítottam pár helyesírási hibát |
|||
| 3. sor: | 3. sor: | ||
{{Vissza|Számítógépes_grafika_és_képfeldolgozás}} | {{Vissza|Számítógépes_grafika_és_képfeldolgozás}} | ||
Ez a tutorial alapvetően azt hivatott elmagyarázni, hogy hogyan kell az OpenGL-t használni. Ebből adódóan még minimális kitérőt se tesz a házik mögött álló elméleti háttér | Ez a tutorial alapvetően azt hivatott elmagyarázni, hogy hogyan kell az OpenGL-t használni. Ebből adódóan még minimális kitérőt se tesz a házik mögött álló elméleti háttér kifejtésére, csak abba ad segítséget, hogy hogyan oldd meg a házidat. A háziknak pont az a célja, hogy az órán elsajátított elméletet gyakorolni tudd, így ha az elmélet ismerete nélkül vágsz neki a házinak, az lehet, hogy sikerülni fog, de a tárgy szempontjából semmi értelme nem lesz... Ez a tutorial nem helyettesíti az órán való jelenlétet. | ||
'''Az oldalról kódot a saját házidba átemelni TILOS! Még ha pár sornyi kódról is van szó, gépeld be szépen magadtól, addig is gyakorlod a dolgot. Meg persze nem is érdemes másolgatni, mert csak borzolod vele a plágiumkereső idegeit, és sokban ronthat az esélyeiden az aláírás megszerzésére''' | '''Az oldalról kódot a saját házidba átemelni TILOS! Még ha pár sornyi kódról is van szó, gépeld be szépen magadtól, addig is gyakorlod a dolgot. Meg persze nem is érdemes másolgatni, mert csak borzolod vele a plágiumkereső idegeit, és sokban ronthat az esélyeiden az aláírás megszerzésére''' | ||
| 13. sor: | 13. sor: | ||
==Az OpenGL és GLUT alapjai== | ==Az OpenGL és GLUT alapjai== | ||
=== Az OpenGL === | === Az OpenGL === | ||
* Az OpenGL egyszerű térbeli alakzatok ( | * Az OpenGL egyszerű térbeli alakzatok (primitívek), pl.: pontok, vonalak, háromszögek rajzolására specializálódott. | ||
** Ezekből az építőkockákból csodákat lehet művelni, a mai számítógépes játékok nagyrészt | ** Ezekből az építőkockákból csodákat lehet művelni, a mai számítógépes játékok nagyrészt hárszömszögek rajzolásából építkeznek. | ||
** A primitíveket nagyon | ** A primitíveket nagyon sokféleképpen és nagyon hatékonyan lehet az OpenGL-lel kirajzolni, de ezen kívül semmi mást nem tud, nem tud képet betölteni, árnyékokat számolni, de még egy kocka kirajzolásához is "küzdeni" kell. | ||
** A hatékonyság növelése érdekében az OpenGL a videókártyát is használja a rajzoláshoz. | ** A hatékonyság növelése érdekében az OpenGL a videókártyát is használja a rajzoláshoz. | ||
** Egy rajzolófüggvény viselkedése több száz paramétertől függ, persze nem kell az összeset függvény argumentumként átadni, ehelyett az '''OpenGL egy állapotgép'''-en alapszik. | ** Egy rajzolófüggvény viselkedése több száz paramétertől függ, persze nem kell az összeset függvény argumentumként átadni, ehelyett az '''OpenGL egy állapotgép'''-en alapszik. | ||
| 26. sor: | 26. sor: | ||
*** pl.: glDrawElementsInstancedBaseVertexBaseInstance() - amúgy ez a leghosszabb nevű OpenGL függvény, de a tárgyból nincs rá szükség. | *** pl.: glDrawElementsInstancedBaseVertexBaseInstance() - amúgy ez a leghosszabb nevű OpenGL függvény, de a tárgyból nincs rá szükség. | ||
* A grafikában a lebegőpontos számokkal float alakban szeretünk dolgozni. A double-el nem az a gond, hogy kétszer annyi helyet foglal, hanem hogy a double pontosságú műveletvégzés sokkal lassabb, mint ha megelégednénk a float által elvárt pontossággal. Ez az oka annak, hogy a videókártyák, kb 2011-ig csak floatokkal tudtak számolni (double-el csak szoftveresen emulálva, nagyjából 25-ször olyan lassan). Az OpenGL-nek az a verziója, amit a tárgyban kell használni (1.1), csak floatokkal tud dolgozni a videókártyán, ami azt jelenti, hogy ha double-t adsz neki, attól nem csak, hogy nem lesz pontosabb, de még plusz munkát is adsz neki ezzel, mert neki vissza kell kasztolni a számot floattá, és ez a kasztolás nagyon nincs ingyen. A floatok használatának egy további előnye, hogy az x86 processzorok 4 db float műveletet tudnak egyszerre elvégezni SSE-vel. Ez az oka annak, hogy a tárgyból a legtöbb függvénynek csak a floatot váró alakját lehet használni, például a glVertex3f-et lehet, de a glVertex3d-t nem. | * A grafikában a lebegőpontos számokkal float alakban szeretünk dolgozni. A double-el nem az a gond, hogy kétszer annyi helyet foglal, hanem hogy a double pontosságú műveletvégzés sokkal lassabb, mint ha megelégednénk a float által elvárt pontossággal. Ez az oka annak, hogy a videókártyák, kb 2011-ig csak floatokkal tudtak számolni (double-el csak szoftveresen emulálva, nagyjából 25-ször olyan lassan). Az OpenGL-nek az a verziója, amit a tárgyban kell használni (1.1), csak floatokkal tud dolgozni a videókártyán, ami azt jelenti, hogy ha double-t adsz neki, attól nem csak, hogy nem lesz pontosabb, de még plusz munkát is adsz neki ezzel, mert neki vissza kell kasztolni a számot floattá, és ez a kasztolás nagyon nincs ingyen. A floatok használatának egy további előnye, hogy az x86 processzorok 4 db float műveletet tudnak egyszerre elvégezni SSE-vel. Ez az oka annak, hogy a tárgyból a legtöbb függvénynek csak a floatot váró alakját lehet használni, például a glVertex3f-et lehet, de a glVertex3d-t nem. | ||
* Az OpenGL RGB színskálán állítja elő a képet, és neki is RGB | * Az OpenGL RGB színskálán állítja elő a képet, és neki is RGB értéket kell adni, ha egy színt akarunk leírni. A grafikában általában nem a megszokott komponensenként egy byte-on (0, 255) specifikáljuk a színeket. Ezzel alapvetően az baj, hogy a színhez tartozó fényerősség csak nagyon kis tartományon változhat, ahhoz képest amekkora különbségek a valóságban előfordulnak, pl. a Nap színe, vagy éjszaka egy sötét szobának a színei között több mint 10000-szeres fényerősségbeli különbség van. A másik gond, hogy a byte színekkel nehéz műveletet végezni, pl. a valóságban két fehér fény összege egy még fényesebb fehér, míg az egy byte-on leírt színeknél a fehér már önmagából a lehető legvilágosabb szín, amit meg tudunk jeleníteni. Ennek az orvoslására a színeket komponensenként floatokkal írjuk le. Nem véletlen egybeesés, hogy a megvilágítást a videókártya számolja az OpenGL-ben, ami mint tudjuk, floatokkal szeret dolgozni. Egy fényt két dolog is jellemez, az egyik a színe (hullámhossza), ami jelen esetben az RGB komponensek aránya. Ha csak ezt akarjuk megadni, akkor a komponenseket a (0, 1) tartományon írjuk le. De a fényt jellemzi még az erőssége (luminanciája) is, ami független magától a színtől. A fényerősség tetszőlegesen nagy, vagy kicsi, de akár még negatív is lehet. Ha egy fényforrást akarunk leírni, akkor a szín és a fényerősség szorzatára vagyunk kíváncsiak, a (-végtelen, végtelen) tartományon komponensenként (erre a sugárkövetésnél lesz szükség), de ha az OpenGL-nek akarunk megadni egy színt, akkor azt a (0, 1), esetleg a (-1, 1) tartományon tegyük. Technikailag byte-ot is lehet adni az OpenGL-nek, de pedagógia okokból a házikban kötelező float színeket használni. | ||
* Az OpenGL csak a rajzolással foglalkozik, az, hogy hogyan jön létre az a valami (célszerűen egy ablak), amire ő tud rajzolni, az viszont már nem az ő dolga. Itt jön a képbe a GLUT. | * Az OpenGL csak a rajzolással foglalkozik, az, hogy hogyan jön létre az a valami (célszerűen egy ablak), amire ő tud rajzolni, az viszont már nem az ő dolga. Itt jön a képbe a GLUT. | ||
=== A GLUT === | === A GLUT === | ||
* A GLUT egy platformfüggetlen ablak- és eseménykezelő, lényegében egy híd az oprendszer és az OpenGL context között. A GLUT beállításának | * A GLUT egy platformfüggetlen ablak- és eseménykezelő, lényegében egy híd az oprendszer és az OpenGL context között. A GLUT beállításának nagy része a keretben előre meg van írva, csak az eseménykezelő függvényekről kell gondoskodnunk, amiket majd a GLUT meghív (ezek a függvények határozzák meg, hogy mit csinál a programunk). | ||
* GLUT eseménykezelő függvények: | * GLUT eseménykezelő függvények: | ||
** '''onDisplay()''' - a legfontosabb függvény, ide írjuk a képernyő törlését, majd a szükséges rajzoló részeket. Ha valami változás hatására frissíteni szeretnénk a képernyőt, azaz szeretnénk az onDisplay()-t lefuttatni, hívjuk meg a '''glutPostRedisplay()''' függvényt (ne közvetlenül az onDisplay-t!). Fontos hogy az '''onDisplay()-en belül tilos meghívni a glutPostRedisplay()-t,''' az így megírt program elvi hibás (a képernyő mindig érvénytelen marad), ez a beadón nem fog működni. | ** '''onDisplay()''' - a legfontosabb függvény, ide írjuk a képernyő törlését, majd a szükséges rajzoló részeket. Ha valami változás hatására frissíteni szeretnénk a képernyőt, azaz szeretnénk az onDisplay()-t lefuttatni, hívjuk meg a '''glutPostRedisplay()''' függvényt (ne közvetlenül az onDisplay-t!). Fontos hogy az '''onDisplay()-en belül tilos meghívni a glutPostRedisplay()-t,''' az így megírt program elvi hibás (a képernyő mindig érvénytelen marad), ez a beadón nem fog működni. | ||
** '''onInitialization()''' - inicializáló rész, pl. globális változók inicializálására. Tipikus hiba, hogy a globális változóknak egy gl/glu/glut függvény visszatérési értéket adjuk, vagy a változó | ** '''onInitialization()''' - inicializáló rész, pl. globális változók inicializálására. Tipikus hiba, hogy a globális változóknak egy gl/glu/glut függvény visszatérési értéket adjuk, vagy a változó konstruktorában meghívunk ilyen függvényt. Így ugyanis még a main() kezdete előtt futattnánk le egy ilyen típusú függvényt, amikor még ezek a könyvtárak nincsenek inicializálva. Ennek az elkerülésére van ott az onInitialization - ebben már nyugodtan használhatunk bármilyen függvényt az inicializációhoz. | ||
** '''onKeyboard()''' - Itt tudjuk kezelne egy billentyű lenyomását. Erre a házikban általában csak minimális szükség van. | ** '''onKeyboard()''' - Itt tudjuk kezelne egy billentyű lenyomását. Erre a házikban általában csak minimális szükség van. | ||
** '''onMouse()''' - Itt kapunk | ** '''onMouse()''' - 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 (figyelem itt bal felül van az origó!) hol volt. | ||
** '''onMouseMotion()''' - 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. | ** '''onMouseMotion()''' - 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. | ||
** '''onIdle()''' - 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ó). | ** '''onIdle()''' - 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ó). | ||
| 64. sor: | 64. sor: | ||
glEnd(); | glEnd(); | ||
</pre> | </pre> | ||
* A pontok megadásához glVertex{2,3}f függvények valamelyikét kell használnod, az alapján, hogy hány | * A pontok megadásához glVertex{2,3}f függvények valamelyikét kell használnod, az alapján, hogy hány dimenzióban dolgozol. | ||
** Tehát 2 dimenzióban a glVertex2f-et, 3 dimenzióban a glVertex3f-et kell használnod. | ** Tehát 2 dimenzióban a glVertex2f-et, 3 dimenzióban a glVertex3f-et kell használnod. | ||
* A pontok sorrendje nagyon fontos, már egy quad esetében sem lehet "csak úgy" felsorolni a négy pontot, ha rossz sorrendben adjuk meg őket, akkor két egymásba csúszott háromszöget fogunk látni. | * A pontok sorrendje nagyon fontos, már egy quad esetében sem lehet "csak úgy" felsorolni a négy pontot, ha rossz sorrendben adjuk meg őket, akkor két egymásba csúszott háromszöget fogunk látni. | ||
| 70. sor: | 70. sor: | ||
=== 2D rajzolás === | === 2D rajzolás === | ||
* A koordináták amiket átadsz azok a normalizált eszköz | * A koordináták amiket átadsz azok a normalizált eszköz koordináta-rendszerben vannak értelmezve, ahol a (0,0) a képernyő közepe, a (-1, -1) pedig a bal alsó sarok. | ||
** Példaprogram: [http://pastebin.com/zAMBmSz5 Háromszögek] | ** Példaprogram: [http://pastebin.com/zAMBmSz5 Háromszögek] | ||
| 98. sor: | 98. sor: | ||
#define CIRCLE_RESOLUTION 32 | #define CIRCLE_RESOLUTION 32 | ||
// Piros kor, a | // Piros kor, a képernyő bal oldalán | ||
glBegin(GL_TRIANGLE_FAN); { | glBegin(GL_TRIANGLE_FAN); { | ||
float radius = 0.25f, center_x = -0.5f, center_y = 0.4f; | float radius = 0.25f, center_x = -0.5f, center_y = 0.4f; | ||
| 112. sor: | 112. sor: | ||
} glEnd(); | } glEnd(); | ||
// | // Színátmenetes kör, a képernyő jobb oldalán | ||
glBegin(GL_TRIANGLE_FAN); { | glBegin(GL_TRIANGLE_FAN); { | ||
float radius = 0.25f, center_x = 0.5f, center_y = 0.4f; | float radius = 0.25f, center_x = 0.5f, center_y = 0.4f; | ||
| 126. sor: | 126. sor: | ||
} glEnd(); | } glEnd(); | ||
// | // Félkörív | ||
glBegin(GL_LINE_STRIP); { | glBegin(GL_LINE_STRIP); { | ||
float radius = 0.75f, center_x = 0.0f, center_y = 0.0f; | float radius = 0.75f, center_x = 0.0f, center_y = 0.0f; | ||
| 155. sor: | 155. sor: | ||
*** Egér mozgatás - onMouseMotion | *** Egér mozgatás - onMouseMotion | ||
*** Egér gombbal kattintás - onMouse | *** Egér gombbal kattintás - onMouse | ||
*** Az egér eseményekkel kapcsolatban egy apró kellemetlenség, hogy a GLUT a kattintások helyét az oprendszer koordináta rendszerében adja át nekünk (ablak bal fölső sarka az origó, x jobbra, y | *** Az egér eseményekkel kapcsolatban egy apró kellemetlenség, hogy a GLUT a kattintások helyét az oprendszer koordináta rendszerében adja át nekünk (ablak bal fölső sarka az origó, x jobbra, y lefelé nő, az egység pedig a pixel), míg mi normalizált eszközkoordinátákkal dolgozunk (az ablak közepe az origó, a x jobbra, az y felfele nő, és mindkét dimenzióban az ablak méretének a fele az egység). Ezért kénytelenek vagyunk átszámolni azokat az értékeket, amiket a GLUT ad nekünk. Erre egy lehetséges megoldás: | ||
<br/> <syntaxhighlight lang="c"> | <br/> <syntaxhighlight lang="c"> | ||
| 213. sor: | 213. sor: | ||
=== Animáció === | === Animáció === | ||
* Az animáció annyit jelent, hogy az egyes objektumok állapota az időnek is a függvénye. A | * Az animáció annyit jelent, hogy az egyes objektumok állapota az időnek is a függvénye. A pillanatnyi időt a <code> glutGet(GLUT_ELAPSED_TIME); </code> függvényhívással tudjuk lekérdezni, célszerűen az onIdle függvényben. | ||
* Egy mozgó testet legjobban a fizika | * Egy mozgó testet legjobban a fizika törvényeivel tudunk leírni, egy egyenes vonalú egyenletes mozgás leírásához mindössze a <code> v = s / t </code> képletre van szükségünk. | ||
* Az animáció onnantól kezd | * Az animáció onnantól kezd bonyolulttá válni, hogy ha több mozgó test állapota egymástól függ (pl: mikor ütköznek). Ilyenkor ugyanis a korrekt szimuláció egy differenciálegyenlet megoldását jelentené. Ennek egy egyszerű közelítése a diszkrét idő-szimuláció, ahol az ötlet az, hogy válasszunk egy időegységet, amennyi idő alatt a testek állapota csak minimálisan változik meg, ez tipikusan pár milliszekundum, és legfeljebb ilyen időközönként kiválasztott statikus pillanatokban vizsgáljuk csak az egymásra hatásokat. Manapság a számítógépes játékok nagy része is ezt a módszert használja. | ||
* Egyszerű példaprogram: [http://pastebin.com/MASqQWhw Pattogó labda] | * Egyszerű példaprogram: [http://pastebin.com/MASqQWhw Pattogó labda] | ||
| 223. sor: | 223. sor: | ||
void onIdle() { | void onIdle() { | ||
static int last_time = glutGet(GLUT_ELAPSED_TIME); // Visszaadja a jelenlegi időt | static int last_time = glutGet(GLUT_ELAPSED_TIME); // Visszaadja a jelenlegi időt milliszekundumban | ||
int curr_time = glutGet(GLUT_ELAPSED_TIME); | int curr_time = glutGet(GLUT_ELAPSED_TIME); | ||
int diff = curr_time - last_time; // Az előző onIdle óta eltelt idő | int diff = curr_time - last_time; // Az előző onIdle óta eltelt idő | ||
| 230. sor: | 230. sor: | ||
// Két onIdle között eltelt idő nagyon változó tud lenni, és akár elég nagy is lehet | // Két onIdle között eltelt idő nagyon változó tud lenni, és akár elég nagy is lehet | ||
// ahhoz, hogy a labda látványosan bele menjen a falba, mielőtt visszapattan. Ezért | // ahhoz, hogy a labda látványosan bele menjen a falba, mielőtt visszapattan. Ezért | ||
// osszuk fel az | // osszuk fel az eltelt időt kisebb részekre, pl. max 5 milliszekundumos egységekre, | ||
// és ilyen időközönként nézzük meg, hogy a labda ütközött-e a fallal. | // és ilyen időközönként nézzük meg, hogy a labda ütközött-e a fallal. | ||
const int time_step = 5; | const int time_step = 5; | ||
| 268. sor: | 268. sor: | ||
=== 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 | * 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 | * 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, és ezzel ugyanazt a hatást érjük el. Persze nem kell minden egyes pontot nekünk elforgatni, 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 lineáris (és affin) 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 | ** A lineáris (és affin) 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. 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ó. | ||
** Az OpenGL két mátrixot ad nekünk, amiknek módosíthatunk, és amivel az összes kirajzolandó pontot mindig beszorozza helyettünk. Az egyik a GL_MODELVIEW, a másik a GL_PROJECTION. A GL_MODELVIEW-val mozgathatunk objektumokat egymáshoz képest, és itt tudjuk megadni, hogy hogyan kell transzformálni a világot, hogy az origóban lévő, -z irányba néző képzeletbeli kamera azt lássa, amit meg akarunk jeleníteni. A GL_PROJECTION pedig azt adja meg, hogy a kamerával hogyan kell | ** Az OpenGL két mátrixot ad nekünk, amiknek módosíthatunk, és amivel az összes kirajzolandó pontot mindig beszorozza helyettünk. Az egyik a GL_MODELVIEW, a másik a GL_PROJECTION. A GL_MODELVIEW-val mozgathatunk objektumokat egymáshoz képest, és itt tudjuk megadni, hogy hogyan kell transzformálni a világot, hogy az origóban lévő, -z irányba néző képzeletbeli kamera azt lássa, amit meg akarunk jeleníteni. A GL_PROJECTION pedig azt adja meg, hogy a kamerával hogyan kell fényképezni. | ||
*** Két | *** Két dimenzióban a két mátrix különválasztása gyakorlatilag fölösleges, ennek csak 3D-be lesz szerepe, majd a megvilágításkor. 2D-ben a kényelmesebb a GL_PROJECTION-ra bízni a kamera mozgatást is, és a GL_MODELVIEW-t pedig meghagyni csak az objektumok közötti transzformációk leírására. | ||
* Projekció 2D-ben: a fényképezés módja nagyon egyszerű, egyszerűen eldobjuk a 'z' koordinátát, és az x-y | * Projekció 2D-ben: a fényképezés módja nagyon egyszerű, egyszerűen eldobjuk a 'z' koordinátát, és az x-y pozíció alapján rajzolunk. Vagy legalábbis ezt csináltuk eddig, de az NDC nem volt kényelmes. Itt viszont lehetőséget kapunk saját koordináta-rendszer megválasztására, ahol az egység lehet pl. 1 méter, és a kamera pedig mondjuk követhet egy karaktert egy játékban. | ||
* Példaprogram: [http://pastebin.com/d8ZLKiTg Sidescroller] | * Példaprogram: [http://pastebin.com/d8ZLKiTg Sidescroller] | ||
| 281. sor: | 281. sor: | ||
// akarjuk módosítani a transzformációs mátrix műveletekkel. | // akarjuk módosítani a transzformációs mátrix műveletekkel. | ||
glMatrixMode(GL_PROJECTION); | glMatrixMode(GL_PROJECTION); | ||
// Egység mátrixot | // Egység mátrixot töltünk be a jelenleg módosítható | ||
// mátrix helyére (ez a projekciós mátrix). | // mátrix helyére (ez a projekciós mátrix). | ||
glLoadIdentity(); | glLoadIdentity(); | ||
// Ez egy olyan merőleges ( | // Ez egy olyan merőleges (ortogonális) vetítés mátrixát "írja be" a GL_PROJECTION-be, | ||
// aminek | // aminek eredményeképpen a karakter az x tengely mentén középen lesz, és 10 egység | ||
// (méter) széles részt látunk a világból míg az y tengely mentén a képernyő alsó egy | // (méter) széles részt látunk a világból míg az y tengely mentén a képernyő alsó egy | ||
// ötödében lesz, és itt is 10 egység magas részt látunk. Fontos megjegyezni, hogy ez | // ötödében lesz, és itt is 10 egység magas részt látunk. Fontos megjegyezni, hogy ez | ||
| 309. sor: | 309. sor: | ||
* 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. | * 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. | * 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 | * Egy transzformáció meghívásakor annak a mátrixa hozzászorzódik a GL_MODELVIEW mátrixhoz (jobbról). Emlékeztető: a mátrix szorzás, és a mátrix-vektor 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 az autó egyáltalán hol van. | * 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 az autó egyáltalán hol van. | ||
* [http://pastebin.com/0UiK3fVa Példa a transzformációk sorrendjére:] | * [http://pastebin.com/0UiK3fVa Példa a transzformációk sorrendjére:] | ||
| 347. sor: | 347. sor: | ||
*** A jelenleg aktív mátrixot (<code> GL_PROJECTION </code> vagy <code> GL_MODELVIEW </code> ) elmenti annak a stackjébe. | *** A jelenleg aktív mátrixot (<code> GL_PROJECTION </code> vagy <code> GL_MODELVIEW </code> ) elmenti annak a stackjébe. | ||
** <code> glPopMatrix(); </code> | ** <code> glPopMatrix(); </code> | ||
*** A jelenleg aktív mátrix stackjéből a legutóbb elmentett mátrixot | *** A jelenleg aktív mátrix stackjéből a legutóbb elmentett mátrixot visszaállítja. A következő glPopMatrix() mátrix az az előtt elmentett mátrixot állítja vissza. | ||
* Megjegyzések: | * Megjegyzések: | ||
** A <code> GL_MODELVIEW </code> stack mélysége legalább 32, a GL_PROJECTION mátrixé pedig legalább 2. | ** A <code> GL_MODELVIEW </code> stack mélysége legalább 32, a GL_PROJECTION mátrixé pedig legalább 2. | ||
| 364. sor: | 364. sor: | ||
rajzolas2(); // It a világ már nincs elforgatva. | rajzolas2(); // It a világ már nincs elforgatva. | ||
</syntaxhighlight> <br/> | </syntaxhighlight> <br/> | ||
* A | * A mátrix stack hierarchikusan felépülő testek rajzolását nagy mértékben megkönnyíti. | ||
** | ** Hierarchikus test pl: az emberi kéz. | ||
*** Ha a felkarod elforgatod a vállad körül, akkor azzal az alkarod a csuklód és az ujjaid elmozdulnak. Minden | *** Ha a felkarod elforgatod a vállad körül, akkor azzal az alkarod a csuklód és az ujjaid elmozdulnak. Minden ízület transzformációja befolyásolja az összes csontot, ami belőle nő ki, és az összes csontot, ami a belőle kinövő csontokból nő ki, rekurzívan. Ha ezt manuálisan akarnánk leprogramozni, egy olyan fát kéne felépítenünk az ízületekből, ahol a gyerekek száma változó. Ez nem lehetetlen, de is kifejezetten izgalmas, viszont a mátrix stack segítségével ez nagyon egyszerűen megoldható. | ||
*** | *** Pszeudokóddal, feltételezve, hogy origó középpontú, egység hosszú, x-el párhuzamos főtengelyű hengert tudunk rajzolni a "henger kirajzolása" utasítással: | ||
<pre> | <pre> | ||
glPushMatrix(); { | glPushMatrix(); { | ||
| 534. sor: | 534. sor: | ||
* Görbék alatt grafikában olyan függvényeket értünk, amik diszkrét ponthalmazból folytonos ponthalmazt állítanak elő. | * Görbék alatt grafikában olyan függvényeket értünk, amik diszkrét ponthalmazból folytonos ponthalmazt állítanak elő. | ||
** Példa: 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 | ** Példa: 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. 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. | ||
** A | ** 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 paramtére, é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ék egyik legfontosabb tulajdonsága a folytonosság mértéke, vagyis, hogy mekkora az a legnagyobb szám (n), amennyiedik deriváltja még folytonos. Jelölése: c(n). | ** A görbék egyik legfontosabb tulajdonsága a folytonosság mértéke, vagyis, hogy mekkora az a legnagyobb szám (n), amennyiedik deriváltja még folytonos. Jelölése: c(n). | ||
*** c0 folytonos görbe az, aminek szakadása van. Egy valós útvonal esetében ez azt jelenti, hogy valahol | *** c0 folytonos görbe az, aminek szakadása van. Egy valós útvonal esetében ez azt jelenti, hogy valahol teleportálnunk is kell. Tekintve, hogy 3 kocsmát is érinteni akarunk, ez nem tűnik lehetetlennek :) | ||
*** A valós tárgyak mozgása legalább c2 folytonos (azaz a gyorsulás folytonosan változik). Ezt a agyunk megszokta, és a nem c2 folytonos mozgás nem tűnik valóságosnak. Ebből fakadóan az olyan egyszerűbb függvények, mint hogy a pontokat egyenes vonalakkal összekötjük, nem eredményeznek hihető mozgást. | *** A valós tárgyak mozgása legalább c2 folytonos (azaz a gyorsulás folytonosan változik). Ezt a agyunk megszokta, és a nem c2 folytonos mozgás nem tűnik valóságosnak. Ebből fakadóan az olyan egyszerűbb függvények, mint hogy a pontokat egyenes vonalakkal összekötjük, nem eredményeznek hihető mozgást. | ||
* A tipikus görbék amiket a grafikában használni szoktunk: | * A tipikus görbék amiket a grafikában használni szoktunk: | ||
| 545. sor: | 545. sor: | ||
*** Lokálisan vezérelhetőek, nagyon kényelmes velük dolgozni, viszont az implementálásuk általában bonyolult. | *** Lokálisan vezérelhetőek, nagyon kényelmes velük dolgozni, viszont az implementálásuk általában bonyolult. | ||
*** Pl: Kochanek–Bartels görbe | *** Pl: Kochanek–Bartels görbe | ||
**** Gyakori | **** Gyakori implementációja a közelítőleg c2 folytonos Catmull-Rom görbe (Ezt akkor kapjuk, ha a Kochanek–Bartels összes paraméterét nullának választjuk). | ||
* Példaprogram: [http://pastebin.com/iJW80B4t Tenziós Catmull-Rom görbe] | * Példaprogram: [http://pastebin.com/iJW80B4t Tenziós Catmull-Rom görbe] | ||
| 552. sor: | 552. sor: | ||
</div> | </div> | ||
== Régi wikiről áthozott rész, | == Régi wikiről áthozott rész, frissítésre szorul == | ||
=== Projekciós mátrixok, transzformációk === | === Projekciós mátrixok, transzformációk === | ||