Erste Animation

Beispiel

Visual Studio Projekt herunterladen

Unsere erste Animation ist eine sich drehende, archimedische Spirale. Fixiert man das Zentrum der rotierenden Spirale eine Zeit lang und blickt danach auf ein anderes Objekt, so stellt sich ein Bewegungsnacheffekt ein.

Für die Umsetzung verwendet das Beispiel die Spiralklasse CSpiral, die ihre eigene Rotation verwaltet und sich auch selbständig zeichnen kann.

CSpiral

In der Datei spiral.h finden sich folgende Attribute.

    float        m_radius;   // Radius der Spirale
    unsigned int m_steps;    // Zeichnenschritte
    float        m_rotation; // Rotation in Grad

Sie werden wie üblich im Konstruktor initialisiert. Die Attribute sind als geschützt bzw. protected deklariert. Das bedeutet, dass wir von außen nicht auf diese Elemente zugreifen können. Innerhalb der Funktionen der Klasse CSpiral kann allerdings ganz normal auf diese Elemente zugegriffen werden. Die beiden öffentlichen Funktionen

    void rotate(float degrees); // Spirale rotieren
    void render();              // Spirale zeichnen

tun dies auch ausgiebig. Geschützte Attribute können also indirekt über das Aufrufen öffentlich zugänglicher (public) Funtkionen verändert werden. Sehen wir uns dazu die Implementierung der Funtkion rotate in der Datei spiral.cpp an.

void CSpiral::rotate(float degrees)
{
    // Spirale weiter rotieren
    m_rotation += degrees;
}

Sie wird in der display-Funktion der Datei main.cpp aufgerufen, um unsere Animation jeweils um ein Bild weiter zu bewegen. Die Variable m_rotation ist in unserem Fall durch ihren Status z.B. davor geschützt, dass ihr Wert beliebig umgesetzt wird.

Rotation und Zeichnen der Spirale finden in der Funktion render() statt. Werfen wir zunächst einen Blick auf die Rotation:

void CSpiral::render()
{
    // Bisherige Geometrie-Transofmrationen
    // auf Stapel sichern
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

   // Gesamte Geometrie um positive z-Achse rotieren
    glRotatef(m_rotation, 0.0f, 0.0f, 1.0f);

Wie im letzten Beispiel angedeutet, gibt es mehrere Matrizen in OpenGL. Die Modelview-Matrix enthält geometrische Transformationen, wie Rotation, Skalierung und Verschiebung, die auf jeden zu zeichnenden Vektor angewendet werden. Hier ist also der richtige Ansatzpunkt, um unsere Spirale, also die gesamte Geometrie, rotieren zu lassen.

Mit glMatrixMode(GL_MODELVIEW)schalten wir OpenGL in einen Modus, der das Verändern der Modelview-Matrix zulässt. Danach sichern wir die bisherige Matrix mit glPushMatrix() auf dem Stapel. Am Ende der render-Funktion holen wir diese gesicherte Matrix durch glPopMatrix()wieder vom Stapel:

    // Geometrie-Rotation aufheben
    // (alte Transformationen vom Stapel holen)
    glPopMatrix();

} // render()

Wir machen also alle Änderungen an der Matrix rückgängig, die zwischen Push und Pop stattgefunden haben. Diese Änderung ist in unserem Fall die Rotation der Spirale.

Mit Hilfe des Befehls glRotatef können wir die Geometrie um eine beliebige Achse drehen. Das Zentrum der Rotation ist dabei immer der Ursprung. In unserem Beispiel drehen wir die Spirale um m_rotation Grad um die positive z-Achse (0, 0, 1).

Um Geometrien zu skalieren bzw. zu verschieben kann man die Funktionen glScalef bzw. glTranslatef verwenden.

Das eigentliche Zeichnen der archimedischen Spirale findet in der render-Funktion statt. Zunächst wählen wir eine Linienbreite von 5 Pixeln, und beginnen einen Linienzug.

    // Linienbreite auf 5 Pixel setzen
    glLineWidth(5);

    // Linienzug beginnen
    glBegin(GL_LINE_STRIP);

Danach berechnen und setzen wir die Punkte des Linienzugs durch eine for-Schleife.

    for (unsigned int i = 0; i < m_steps; i++)
    {
        // Wert von i zwischen 0 und 1 skalieren
        float fader = float(i) / float(m_steps-1);

        // Winkel und Radius berechnen
        float angle  = fader*60.0f*PI;
        float radius = fader*m_radius;

        // (x,y) Koordinate bestimmen
        // (Kreis mit wachsendem Radius)
        float x = cosf(angle) * radius;
        float y = sinf(angle) * radius;
       
        // Vektor, durch den die Linie
        // gezogen werden soll, setzen
        glVertex2f(x, y);
    }

Wem renderCircle() aus dem letzten Beispiel noch in Erinnerung ist: Wir zeichnen hier im Grunde Kreisbahnen mit wachsendem Radius.

Zuerst skalieren wir den Wert von i zwischen Null und Eins. Danach lassen wir hiermit einen Winkel von 0 bis 60*PI laufen. Eine Kreisumdrehung entspricht 2*PI. Der Winkel macht also dreißig vollständige Umdrehungen beim Durchlaufen der for-Schleife.

Der Radius, um den wir unsere Punkte vom Ursprung entfernen, lassen wir linear von Null bis m_radius anwachsen. Die (x,y)-Koordinaten ergeben sich dann aus den Kreisfunktionen Sinus und Cosinus, skaliert um den entsprechenden Radius.

Nach Abarbeiten der Schleife, die unsere Punkte setzt, beenden wir unseren Linienzug durch

    // Ende des Linienzuges
    glEnd();
main

Die Datei main.cpp unterscheidet sich nur wenig von derjenigen des letzten Beispiels. Die Unterschiede sind:

  • Es gibt eine globale Variable spiral, auf die von allen Funtkionen in main.cpp zugegriffen werden kann.
  • In der display-Funktion wird die Animation der Spirale über deren Funtkion rotate kontrolliert, unser Daumenkino also um jeweils einen Schritt weiter bewegt.
  • Die Spirale wird dort durch Aufruf der Funktion render gezeichnet.
  • Am Ende der display-Funktion wird mit Hilfe des Befehls glutPostRedisplay() ein erneutes Zeichnen veranlasst. Auf diese Weise springt GLUT bei der nächsten Gelegenheit wieder in unsere display-Funktion, um das nächste Bild zu zeichnen.

Selbständige Programmierung

  • Was ist der Unterschied zwischen einer Rotation, gefolgt von einer Verschiebung gegenüber einer Verschiebung, gefolgt von einer Rotation? Programmiere beide Varianten mit Hilfe verschiedener Abfolgen von glRotatef und glTranslatef und interpretiere das Ergebnis.
  • Wie oben erwähnt findet die Rotation um eine Achse immer um den Ursprung statt. Durch welche Kombination von Verschiebungen und Rotationen kann man das Rotationszentrum an eine beliebige Stelle innerhalb des Objekts verschieben?
  • Programmiere eine logarithmische Spirale. Bei der logarithmischen Spirale wächst der Radius nicht linear, sondern exponentiell nach der Formel radius = b * ea * fader mit zwei Konstanten a und b. ex lässt sich durch die Funktion exp(x) berechnen.
  • Eine Frage: Was passiert, wenn mehr als sechzig Bilder pro Sekunde dargestellt werden? Wie könnte man unter diesen Umständen die Geschwindigkeit der Spirale trotzdem konstant halten? Mehr dazu später.