mario::konrad
programming / C++ / sailing / nerd stuff
OpenGL: Tutorial 4 - Animation / Double Buffering
© 2004 / Mario Konrad

Beschreibung

Das Double-Buffering kommt in diesem Tutorial schön zur Geltung, da dadurch ein Flackern der Animation Verhindert wird. Als Beobachter sich durch eine dreidimensionale Welt zu bewegen ist der Inhalt dieses Tutorials.

Platformen:

Download

Die auf dieser Seite aufgeführten Sourcecodes dürfen uneingeschränkt verwendet, kopiert, verändert und publiziert werden. Jegliche Haftung wird abgelehnt, die Verwendung des hier publizierten Materials geschieht auf eigenes Risiko.

Source: tutorial04.cpp

Paket: tutorial04.tgz

Build und Start

Linux:

$ tar -xzf tutorial04.tgz
$ cd tutorial04
$ make -f Makefile.linux
$ ./tutorial04

Windows/Cygwin:

$ tar -xzf tutorial04.tgz
$ cd tutorial04
$ make -f Makefile.cygwin
$ ./tutorial04

Das Demo

Dieses Demo zeigt zwei Kugeln unterschiedlicher Farbe. Mit den Tasten w, s, a und d kann die Ansicht so verändert werden als, würde man vorwärts oder rückwärts laufen, bzw. sich nach links und rechts drehen.

Das Programm

Dieses Programm unterscheidet sich nicht wesentlich von den vorangegangenen Demos. Allerdings ist dieser Code von Grund auf neu geschrieben. Natürlich unterscheidet es sich nicht wesentlich von seinen Vorgängern.

Wie gewohnt brauchen wir ein paar header files:

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <math.h>

Da wir uns bewegen wollen brauchen wir eine Position eye[3] und eine Blickrichtung dir[3] die jeweils als dreidimensionaler Vektor definiert sind. Die Startposition ist (0,0,15), d.h. in der z-Achse entlang aus dem Bildschirm heraus. Die Blickrichtung ist (0,0,-1), d.h. in Richtung des Ursprungs.

GLfloat eye[3] = { 0.0, 0.0, 15.0 };
GLfloat dir[3] = { 0.0, 0.0, -1.0 };

Auch in diesem Demo brauchen wir wieder eine Funktion die das Zeichenen übernimmt. Entsprechende lokale Variablen werden gleich definiert. Die Löschung der Farbbits und der Tiefenbits kennen wir schon von früheren Demos, wie auch das Laden der Einheitsmatrix.

void display(void)
{
    GLfloat center[3];

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

Um die Position des Betrachters anzugeben bedienen wir uns der Funktion gluLookAt. Diese Benötigt die Position, den zu betrachtenden Punkt und den up-Vektor. Da unser Betrachter definiert ist als Position und Blickrichtung (eye und dir), müssen wir den Betrachtungspunkt zuerst berechnen. Keine Angst, eine einfache Vektor-Addition ist hier völlig ausreichend.

    center[0] = eye[0] + dir[0];
    center[1] = eye[1] + dir[1];
    center[2] = eye[2] + dir[2];

    gluLookAt(
        eye[0], eye[1], eye[2],
        center[0], center[1], center[2],
        0.0, 1.0,  0.0);    // up

Der Rest der Funktion ist mitlerweile ja trivial: zeichnen der Kugeln und tauschen der Puffer (wir nutzen ja double-buffering).

    // draw first sphere
    glPushMatrix();
    glTranslatef(3.0, 0.0, 0.0);
    glColor4f(0.7, 0.4, 0.4, 1.0);
    glutWireSphere(2.0, 12, 12);
    glPopMatrix();

    // draw second sphere
    glPushMatrix();
    glTranslatef(-3.0, 0.0, 0.0);
    glColor4f(0.4, 0.4, 0.7, 1.0);
    glutWireSphere(2.0, 12, 12);
    glPopMatrix();

    glFlush();
    glutSwapBuffers();
}

Auch die Funktion reshape bietet keine neuen Überraschungen.

void reshape(int w, int h)
{
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (double)w / (double)h, 0.1, 100.0);

    glMatrixMode(GL_MODELVIEW);
}

Die Funktion keyboard ist neu. Sie definiert was passieren soll, wenn eine normale Taste gedrückt wird (keine Spezialtasten wie Cursor, Home, End, etc.). Zu Beginn werden wie immer alle benötigten lokalen Variablen definiert.

void keyboard(unsigned char key, int x, int y)
{
    GLfloat a[3];
    GLfloat angle = 5.0 * M_PI / 180.0;

Die folgende Fallunterscheidung behandelt die verschiedenen Tasten.

    switch (key)
    {

Die Tasten w und s bewegen den Beobachter einen Schritt in die Blickrichtung vor bzw. zurück. Nichts Spektakuläres.

        case 'w':
            eye[0] += dir[0];
            eye[1] += dir[1];
            eye[2] += dir[2];
            glutPostRedisplay();
            break;
        case 's':
            eye[0] -= dir[0];
            eye[1] -= dir[1];
            eye[2] -= dir[2];
            glutPostRedisplay();
            break;

Interessanter wird es bei den Tasten a und d. Diese bewirken, dass sich der Beobachter um die y-Achse nach links bzw. nach rechts dreht. Dies wird erreicht durch die Multiplikation des Blickrichtungsvektors mit der Rotationsmatrix. Allgemein ausgedrückt heisst das:

dir=dir*Rydir' = dir * Ry

wobei Ry der Rotationsmatrix um die y-Achse ist:

 cos(phi)   0   sin(phi)
        0   1          0
-sin(phi)   0   cos(phi)

phi ist der Drehwinkel.

        case 'a':
            a[0] =  dir[0] * cos(angle) + dir[2] * sin(angle);
            a[1] =  dir[1];
            a[2] = -dir[0] * sin(angle) + dir[2] * cos(angle);
            dir[0] = a[0];
            dir[1] = a[1];
            dir[2] = a[2];
            glutPostRedisplay();
            break;
        case 'd':
            a[0] =  dir[0] * cos(-angle) + dir[2] * sin(-angle);
            a[1] =  dir[1];
            a[2] = -dir[0] * sin(-angle) + dir[2] * cos(-angle);
            dir[0] = a[0];
            dir[1] = a[1];
            dir[2] = a[2];
            glutPostRedisplay();
            break;

Einfachheitshalber beenden wir das Programm beim Betätigen der ESC Taste.

        case 27: exit(0);
    }
}

Auf die Funktion main und die enthaltene Initialisierung muss nicht mehr eingegangen werden. Falls noch Unklarheiten bestehen, bitte die vorangegangenen Demos durchgehen.

int main(int argc, char ** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(300, 300);
    glutCreateWindow("OpenGL Demo 4");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);

    glutMainLoop();
    return 0;
}