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.

ship

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();
}