Transzformációk, eseménykezelők
1. Hozzunk létre egy új projektet, a múlt órához hasonlóan!
2. Írjuk meg (értsd: COPY/PASTE) a program vázát:
#include <GL/glut.h> void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glutSwapBuffers(); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutCreateWindow("Sailing"); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glutDisplayFunc(display); glutIdleFunc(display); glutMainLoop(); return 0; }
3. Rajzoljunk ki valami érdekeset:
void ship(void) { static float vx[][3] = {0,0,0, 4.2f, 0, 0.7f, 2.550074f, -1.138804f, 1, -1.7f, 0, 5, -1.7f, 0, 0.05f, -1.8f, 0, 0.05f, -1.799999f, 0, 5, 0.231852f, -0.517638f, 6.1f, 2.550074f, -1.138804f, 1, -1.7f, 0, 1, -1.7f, 0, 4.7f, 0, 0.6f, 0, 0, -0.6f, 0, -2, -0.531865f, 0.05f, -2, 0.531865f, 0.05f, -4, 0, 0.2f, 2, 0.52754f, 0.05f, 2, -0.52754f, 0.05f, 4, -0.368479f, 0.2f, 4, 0.368479f, 0.2f, 0, -1, 0.5f, -2, -0.886441f, 0.55f, 0, 1, 0.5f, -2, 0.886441f, 0.55f, -4.5f, 0, 0.7f, 2, -0.879233f, 0.55f, 2, 0.879233f, 0.55f, 4.2f, -0.614131f, 0.7f, 4.2f, 0.614131f, 0.7f }; glBegin(GL_QUADS); // Arboc glVertex3fv(vx[3]); glVertex3fv(vx[6]); glVertex3fv(vx[5]); glVertex3fv(vx[4]); // Vitorla glVertex3fv(vx[9]); glVertex3fv(vx[8]); glVertex3fv(vx[7]); glVertex3fv(vx[10]); // Torzs glVertex3fv(vx[11]);glVertex3fv(vx[14]);glVertex3fv(vx[13]);glVertex3fv(vx[12]); glVertex3fv(vx[11]);glVertex3fv(vx[12]);glVertex3fv(vx[17]);glVertex3fv(vx[16]); glVertex3fv(vx[16]);glVertex3fv(vx[17]);glVertex3fv(vx[18]);glVertex3fv(vx[19]); glVertex3fv(vx[12]);glVertex3fv(vx[13]);glVertex3fv(vx[21]);glVertex3fv(vx[20]); glVertex3fv(vx[14]);glVertex3fv(vx[11]);glVertex3fv(vx[22]);glVertex3fv(vx[23]); glVertex3fv(vx[15]);glVertex3fv(vx[14]);glVertex3fv(vx[23]);glVertex3fv(vx[24]); glVertex3fv(vx[13]);glVertex3fv(vx[15]);glVertex3fv(vx[24]);glVertex3fv(vx[21]); glVertex3fv(vx[17]);glVertex3fv(vx[12]);glVertex3fv(vx[20]);glVertex3fv(vx[25]); glVertex3fv(vx[11]);glVertex3fv(vx[16]);glVertex3fv(vx[26]);glVertex3fv(vx[22]); glVertex3fv(vx[18]);glVertex3fv(vx[17]);glVertex3fv(vx[25]);glVertex3fv(vx[27]); glVertex3fv(vx[19]);glVertex3fv(vx[18]);glVertex3fv(vx[27]);glVertex3fv(vx[28]); glVertex3fv(vx[16]);glVertex3fv(vx[19]);glVertex3fv(vx[28]);glVertex3fv(vx[26]); glEnd(); glBegin(GL_TRIANGLES); // Torzs meg mindig glVertex3fv(vx[13]);glVertex3fv(vx[14]);glVertex3fv(vx[15]); glEnd(); glBegin(GL_LINES); // Kotelzet glVertex3fv(vx[1]);glVertex3fv(vx[2]); glEnd(); }
display-ben:
void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ship(); glutSwapBuffers(); }
4. Szokás szerint nem sok látszik még. A nézeten kellene állítani, de mivel nincs külön nézeti transzformáció, ezért a MODELVIEW mátrixot fogjuk használni:
void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, -12, 5, 0, 10, 0, 0, 1, 0); ship(); glutSwapBuffers(); }
A gluLookAt honnan/hova/felfele hármassal létrehoz egy nézeti transzformációt, és az aktuális mátrixverembe helyezi.
A modell így a kirajzolt területen kívülre kerül, ezért nem látszik semmi. Hogy megjelenjen, meg kell adnunk egy projektív transzformációt is. Szerencsére ehhez már van külön mátrix:
void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, 1, 0.1f, 20); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, -12, 5, 0, 10, 0, 0, 1, 0); ship(); glutSwapBuffers(); }
A gluPerspective segítségével a nézeti csonkagúla határozható meg.
5. Hogy kicsit jobban nézzen ki (ami fények nélkül azért nehéz lesz), legyen a törzs, az árboc, a vitorla és a kötél más-más színű:
void ship(void) { static float vx[][3] = {0,0,0, 4.2f, 0, 0.7f, 2.550074f, -1.138804f, 1, -1.7f, 0, 5, -1.7f, 0, 0.05f, -1.8f, 0, 0.05f, -1.799999f, 0, 5, 0.231852f, -0.517638f, 6.1f, 2.550074f, -1.138804f, 1, -1.7f, 0, 1, -1.7f, 0, 4.7f, 0, 0.6f, 0, 0, -0.6f, 0, -2, -0.531865f, 0.05f, -2, 0.531865f, 0.05f, -4, 0, 0.2f, 2, 0.52754f, 0.05f, 2, -0.52754f, 0.05f, 4, -0.368479f, 0.2f, 4, 0.368479f, 0.2f, 0, -1, 0.5f, -2, -0.886441f, 0.55f, 0, 1, 0.5f, -2, 0.886441f, 0.55f, -4.5f, 0, 0.7f, 2, -0.879233f, 0.55f, 2, 0.879233f, 0.55f, 4.2f, -0.614131f, 0.7f, 4.2f, 0.614131f, 0.7f }; glBegin(GL_QUADS); // Arboc glColor3f(0.95703125f, 0.8671875f, 0.69921875f); glVertex3fv(vx[3]); glVertex3fv(vx[6]); glVertex3fv(vx[5]); glVertex3fv(vx[4]); // Vitorla glColor3f(1, 1, 1); glVertex3fv(vx[9]); glVertex3fv(vx[8]); glVertex3fv(vx[7]); glVertex3fv(vx[10]); // Torzs glColor3f(0.75f, 0.25f, 0); glVertex3fv(vx[11]);glVertex3fv(vx[14]);glVertex3fv(vx[13]);glVertex3fv(vx[12]); glVertex3fv(vx[11]);glVertex3fv(vx[12]);glVertex3fv(vx[17]);glVertex3fv(vx[16]); glVertex3fv(vx[16]);glVertex3fv(vx[17]);glVertex3fv(vx[18]);glVertex3fv(vx[19]); glVertex3fv(vx[12]);glVertex3fv(vx[13]);glVertex3fv(vx[21]);glVertex3fv(vx[20]); glVertex3fv(vx[14]);glVertex3fv(vx[11]);glVertex3fv(vx[22]);glVertex3fv(vx[23]); glVertex3fv(vx[15]);glVertex3fv(vx[14]);glVertex3fv(vx[23]);glVertex3fv(vx[24]); glVertex3fv(vx[13]);glVertex3fv(vx[15]);glVertex3fv(vx[24]);glVertex3fv(vx[21]); glVertex3fv(vx[17]);glVertex3fv(vx[12]);glVertex3fv(vx[20]);glVertex3fv(vx[25]); glVertex3fv(vx[11]);glVertex3fv(vx[16]);glVertex3fv(vx[26]);glVertex3fv(vx[22]); glVertex3fv(vx[18]);glVertex3fv(vx[17]);glVertex3fv(vx[25]);glVertex3fv(vx[27]); glVertex3fv(vx[19]);glVertex3fv(vx[18]);glVertex3fv(vx[27]);glVertex3fv(vx[28]); glVertex3fv(vx[16]);glVertex3fv(vx[19]);glVertex3fv(vx[28]);glVertex3fv(vx[26]); glEnd(); glBegin(GL_TRIANGLES); // Torzs meg mindig glVertex3fv(vx[13]);glVertex3fv(vx[14]);glVertex3fv(vx[15]); glEnd(); glBegin(GL_LINES); // Kotelzet glColor3f(0, 0.5f, 1); glVertex3fv(vx[1]);glVertex3fv(vx[2]); glEnd(); }
6. Rajzoljunk a hajó alá tengert!
A hajó vízvonala Z=0.1-nél van, és a "tenger" legyen 50x50-es!
A rajzoláshoz használjunk egy átméretezett, és eltolt <-1,-1,0>-<1,1,0>-es négyzetet!
void sea(void) { glTranslatef(0, 0, 0.1f); glScalef(50, 50, 1); glBegin(GL_QUADS); glColor3f(0, 0.5f, 1); glVertex3f(-1,-1, 0); glVertex3f(-1, 1, 0); glVertex3f( 1, 1, 0); glVertex3f( 1,-1, 0); glEnd(); }
Figyeljük meg, hogy nem mindegy, hogy milyen sorrendben hívjuk meg a sea és a ship függvényeket!
Ha a sea-t hívjuk meg először, akkor az ottani transzformációk hatnak majd a hajóra is. A legtöbb esetben nem megoldás, ami most, hogy megcseréljük őket. Az aktuális transzformáció letárolásához hívjuk meg a glPushMatrix függvényt, és a visszaállításhoz a glPopMatrix-ot.
Ha átméretezzük az ablakot, a hajó össze-vissza torzul. Oldjuk meg, hogy a hajó arányai állandóak maradjanak!
Ehhez a glutReshapeFunc eseménykezelő, és a gluPerspective aspect paramétere használható.
Az aspect adja meg az aktuális ablak méretarányát a nézeti csonkagúlának, és a glutReshapeFunc-kal adható meg egy eseménykezelő, ami az ablak átméretezésekor hívásra kerül.
Legyen ez a függvény:
void reshape(int width, int height) { glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, 1.0f*width/height, 0.1f, 20); }
A glViewport hívás határozza meg az ablak rajzolásra használt részét. (Ha nem adunk meg Reshape Function-t ez akkor is meghívódik automatikusan minden átméretezéskor)
A projekciós mátrix beállításait ilyenkor törölni kell a display-ből (és minden egyéb helyről).
A main-be szükség van a következő hívásra:
glutReshapeFunc(reshape);
Lehessen billentyűzettel irányítani a hajót!
Mozogjon egy állandó körpályán (ami elég valószínűtlen ugyan), aminek a sugarát lehet állítani, és a forgási sebességet!
Szükségünk lesz pár globális változóra (nem szép! jobb lenne osztályokat használni...):
float rtspd = 1; float rot = 0; float rad = 2;
Írjuk meg a billentyűzetkezelő függvényt:
void keyboard(unsigned char key, int x, int y) { switch (key) { case '+': rad += 2; break; case '-': rad -= 2; break; case 'a': rtspd += 1; break; case 'z': rtspd -= rtspd > 1 ? 1 : 0; break; } }
És adjuk meg a main-ben:
glutKeyboardFunc(keyboard);
Végül használjuk is fel az értékeket a rajzoláshoz:
void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, -12, 5, 0, 10, 0, 0, 1, 0); glPushMatrix(); glRotatef(rot, 0,0,1); rot += rtspd; glTranslatef(0,rad,0); ship(); glPopMatrix(); sea(); glutSwapBuffers(); }