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

Rohamcsiga (vitalap | szerkesztései)
Rohamcsiga (vitalap | szerkesztései)
38. sor: 38. sor:
=== 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.
Az OpenGL néhány csak néhány típusú primitívet tud rajzolni, ezekből kell építkeznünk. A típusok:
* A típusok:
* Pontok: <code>GL_POINTS</code>
** Pontok: GL_POINTS
* Vonalak: <code>GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP</code>
** Vonalak: GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP
* Háromszögek: <code>GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_LOOP</code>
** Háromszögek: GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_LOOP
* Háromszögekből összetett alakzatok:
** Háromszögekből összetett alakzatok:
** Négyszögek (igazándiból 2 háromszög): <code>GL_QUADS, GL_QUAD_STRIP</code>
*** Négyszögek (igazándiból 2 háromszög): GL_QUADS, GL_QUAD_STRIP
** Sokszög (ez is háromszögenként rajzolódik ki): <code>GL_POLYGON</code>
*** Sokszög (ez is háromszögenként rajzolódik ki): GL_POLYGON


<div style="text-align:center;margin:0px auto;">
<div style="text-align:center;margin:0px auto;">
51. sor: 50. sor:
</div>
</div>


* A rajzolás az alábbi módon történik:
A rajzolás az alábbi módon történik:
<pre>
<pre>
glBegin("Primitív típus");
glBegin("Primitív típus");
59. sor: 58. 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 dimenzióban dolgozol.
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.


=== 2D rajzolás ===
=== 2D rajzolás ===


* 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.
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: [[Média:Grafpp_haromszogek.cpp|Háromszögek]]
 
Példaprogram: [[Média:Grafpp_haromszogek.cpp|Háromszögek]]


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
87. sor: 87. sor:
</div>
</div>
<br/>
<br/>
* Minden egyes ponthoz külön színt is tudunk megadni. A glColor3f()-el lehet állítani a rajzolószínt, ami utána az összes glVertex hívásra érvényes lesz. Az összetettebb alakzatoknál az egyes pontok színei interpolálódnak, és szép színátmenetet kapunk.
Minden egyes ponthoz külön színt is meg tudunk megadni. A glColor3f()-el lehet állítani a rajzolószínt, ami utána az összes glVertex hívásra érvényes lesz. Az összetettebb alakzatoknál az egyes pontok színei interpolálódnak, és szép színátmenetet kapunk.
** Példaprogram: [[Média:Grafpp_smiley.cpp|Smiley]]
 
Példaprogram: [[Média:Grafpp_smiley.cpp|Smiley]]


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
141. sor: 142. sor:
=== Eseménykezelés ===
=== Eseménykezelés ===


* A grafikus programok általában eseményvezéreltek, azaz a felhasználó által generált események (pl. egérkattintás) vezérlik a programot. A GLUT ehhez nagyon sok segítséget ad, nekünk csak be kell regisztrálnunk egy-egy függvényt, hogy melyik esemény hatására mi történjen, pl az egérkattintásokat az onMouse() függvényben kezeljük.
A grafikus programok általában eseményvezéreltek, azaz a felhasználó által generált események (pl. egérkattintás) irányítják a programot. A GLUT ehhez nagyon sok segítséget ad, nekünk csak be kell regisztrálnunk egy-egy függvényt, hogy melyik esemény hatására mi történjen, pl az egérkattintásokat az onMouse() függvényben kezeljük.
* A három legfontosabb eseménytípus:
 
** Billentyűzet esemény:  
A három legfontosabb eseménytípus:
*** Billentyű lenyomás - onKeyboard
* Billentyűzet esemény:  
*** Billentyű felengedés - onKeyboardUp
** Billentyű lenyomás - onKeyboard
** Idő múlása (lényegében ez is egy esemény) - onIdle
** Billentyű felengedés - onKeyboardUp
** Egér esemény:
* Idő múlása (lényegében ez is egy esemény) - onIdle
*** Egér mozgatás - onMouseMotion
* Egér esemény:
*** Egér gombbal kattintás - onMouse
** Egér mozgatás - onMouseMotion
*** 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:
** 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 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">
167. sor: 169. sor:
</syntaxhighlight> <br/>
</syntaxhighlight> <br/>


* Példa: [[Média:Grafpp_rajzoloprogram.cpp‎|Egyszerű rajzolóprogram]]
Példa: [[Média:Grafpp_rajzoloprogram.cpp‎|Egyszerű rajzolóprogram]]


<br/> <syntaxhighlight lang="c">
<br/> <syntaxhighlight lang="c">
void onMouse(int button, int state, int x, int y) {
void onMouse(int button, int state, int x, int y) {
   if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
   if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
     glClear(GL_COLOR_BUFFER_BIT); // Jobb klikkre toroljuk a kepernyot.
     glClear(GL_COLOR_BUFFER_BIT); // Jobb klikkre töröljuk a képernyőt.
     glutPostRedisplay(); // Szolunk, hogy az ablak tartalma megvaltozott, kerjuk a GLUT-ot, hogy hívja meg az onDisplay-t.
     glutPostRedisplay(); // Szólunk, hogy az ablak tartalma megváltozott, kérjuk a GLUT-ot, hogy hívja meg az onDisplay-t.
   } else if(button == GLUT_LEFT_BUTTON) { // Ha a bal gomb allapota megvaltozott.
   } else if(button == GLUT_LEFT_BUTTON) { // Ha a bal gomb állapota megváltozott.
     if(state == GLUT_DOWN) {
     if(state == GLUT_DOWN) {
       drawing = true; // Ha lenyomtuk akkor rajzolni akarunk.
       drawing = true; // Ha lenyomtuk akkor rajzolni akarunk.
       Vector pos = convertToNdc(x, y); // Atvaltjuk a pontot.
       Vector pos = convertToNdc(x, y); // Átváltjuk a pontot.
       glBegin(GL_POINTS); { // Kirajzoljuk.
       glBegin(GL_POINTS); { // Kirajzoljuk.
         glVertex2f(pos.x, pos.y);
         glVertex2f(pos.x, pos.y);
       } glEnd();
       } glEnd();
       last_mouse_pos = pos; // Elmentjuk, hogy az elso szakasz, majd ebbol a pontbol indul.
       last_mouse_pos = pos; // Elmentjük, hogy az első szakasz, majd ebből a pontból indul.
       glutPostRedisplay(); // Szolunk, hogy az ablak megvaltozott, kerjuk az ujrarajzolasat.
       glutPostRedisplay(); // Szolunk, hogy az ablak megváltozott, kérjük az újrarajzolását.
     } else if(state == GLUT_UP) {
     } else if(state == GLUT_UP) {
       drawing = false; // Ha most engedtuk fel, akkor mar nem akarunk rajzolni.
       drawing = false; // Ha most engedtük fel, akkor mar nem akarunk rajzolni.
     }
     }
   }
   }
191. sor: 193. sor:
void onMouseMotion(int x, int y) {
void onMouseMotion(int x, int y) {
   if(drawing) {
   if(drawing) {
     Vector pos = convertToNdc(x, y); // Kiszamoljuk az eger jelenlegi helyzetet NDC-ben.
     Vector pos = convertToNdc(x, y); // Kiszámoljuk az egér jelenlegi helyzetet NDC-ben.
     glBegin(GL_LINES); { // Kirajzolunk egy vonalat az elozo es a mostani helyzete koze.
     glBegin(GL_LINES); { // Kirajzolunk egy vonalat az előző és a mostani helyzete közé.
       glVertex2f(last_mouse_pos.x, last_mouse_pos.y);
       glVertex2f(last_mouse_pos.x, last_mouse_pos.y);
       glVertex2f(pos.x, pos.y);
       glVertex2f(pos.x, pos.y);
     } glEnd();
     } glEnd();
     glutPostRedisplay(); // Szolunk, hogy az ablak megvaltozott, kerjuk az ujrarajzolasat.
     glutPostRedisplay(); // Szólunk, hogy az ablak megváltozott, kérjük az újrarajzolását.
     last_mouse_pos = pos; // Frissitjuk a elozo helyzetet.
     last_mouse_pos = pos; // Frissítjük a előző helyzetet.
   }
   }
}
}
208. sor: 210. sor:


=== Animáció ===
=== Animáció ===
* 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 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ó 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. Az animáció alatt leggyakrabban testek mozgását, néha testek deformálódását értjük. 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 bonyolulttá válni, amikor több mozgó test állapota egymástól függ (például ütköznek egymással). Ilyenkor ugyanis a korrekt szimuláció egy differenciálegyenlet megoldását jelentené. Numerikus módszerekkel viszont nem szeretünk diffegyenletet megoldani. Ennek a problémának 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. Így a képletek nagyon leegyszerűsödnek, és semmi szükség nem lesz differenciálegyenletekre. Manapság a számítógépes játékok nagy része is ezt a módszert használja.
* 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: [[Média:Grafpp_pattogo_labda.cpp|Pattogó labda]]
* Egyszerű példaprogram: [[Média:Grafpp_pattogo_labda.cpp|Pattogó labda]]