OpenGL.org 3Dsource.de: Infos rund um 3D-Computergrafik, OpenGL und VRML
space Telefon-Tarife space VDI - Arbeitskreis Fahrzeugtechnik space Beschallungsanlagen und Elektroakustik space Heise-News space

8 Arbeiten mit Viewing- und Kameratransformationen und gluLookAt()

8.010 Wie arbeitet die Kamera (meinen Augpunkt) in OpenGL ?

Soweit es OpenGL betrifft, gibt es eigentlich keine (bewegliche) Kamera. Genauer, die Kamera ist immer fest im Ursprung des Weltkoordinatensystems (0, 0, 0) installiert. Um den Eindruck einer sich bewegenden Kamera zu schaffen, muss OpenGL deshalb die gesamte Szene relativ zum Augpunkt verschieben und zwar in der jeweils der gewünschten Kamerabewegung entgegengesetzten Richtung.

8.020 Wie kann ich mein Auge bzw. die Kamera in meiner Szene bewegen ?

OpenGL bietet keinen direkten Zugriff auf die Kameraposition. Mit der GLU Funktion gluLookAt() ist es aber möglich die Kamera bzw. die Szene relativ dazu zu positionieren.

    gluLookAt(kameraX, kameraY, kameraZ, /* hier stehe ich */
              szeneX,  szeneY,  szeneZ,  /* hier schaue ich hin */
              obenX,   obenY,   obenZ);  /* dieser Vektor zeigt senkrecht nach oben */
Diese Funktion berechnet aus den Parametern die notwendigen Transformationen der Szene. Der Aufruf sollte am Anfang der Zeichenroutine, vor eigenen Transformationen, erfolgen.

8.030 Wie soll meine Kamera arbeiten, mit der Modell- oder mit der Projektionsmatrix ?

Der GL_PROJECTION Matrix Stack sollte nur für die Projektionstransformationen genutzt werden, die für die Projektion unbedingt benötigt werden.

    void myResize() /* Aufruf beim Programmstart und nach Resize */
    {
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(ViewingAngle, WindowAspect, nearPlane, farPlane);
      glMatrixMode(GL_MODELVIEW); /* alles andere beeinflusst den ModelView Stack */
    }

Der GL_MODELVIEW Matrix Stack sollte dagegen alle die Transformationen beinhalten, die Objekte oder die Kamera betreffen. Das heisst, auch die Positionierung der Kamera ist auf dem GL_MODELVIEW Stack durchzuführen.

Mit anderen Worten, die GL_PROJECTION Matrix beschreibt die technischen Daten der Kamera wie Brennweite (Öffnungswinkel) und Zoom (nearPlane, farPlane) und Verzerrung (Höhe-Breite Verhältnis).
Die GL_MODELVIEW Matrix beschreibt dagegen den Ort und die Richtung der Kamera.

Die Game Developer FAQ hat weitere Informationen über Matrizen.

Steve Bakers Artikel bietet ebenfalls sehr gute Informationen.

8.040 Wie kann ich eine Zoom-Funktion realisieren ?

Eine einfache Möglichkeit des Zooms lässt sich mittels glScale() im ModelView Modus realisieren. Allerdings können die Objekte leicht ausserhalb des Viewing Volumens (zNear, zFar) geraten und damit abgeschnitten werden.

Die bessere Methode besteht darin, das Viewing Volumen so zu verkleinern, dass die darin befindlichen Objekte grösser auf dem Bildschirm erscheinen. Dieses lässt sich über die Projektionsmatrix realisieren.

Als Beispiel soll unser Programm den Zoomfaktor als Floatzahl berücksichtigen, der durch den Anwender beeinflusst werden kann. Bei einem Wert von 1.0 wird die Originalgrösse beibehalten, grössere Werte verkleinern das Viewing Volumen (simulieren ein Heranzoomen), kleinere Werte bewirken das Gegenteil. Eine Programmteil könnte etwa so aussehen:

    static float zoomFactor; /* globaler Wert. wird durch Anwender geaendert. Startwert 1.0 */

    /* Eine Funktion zum Veraendern der Projektionsmatrix. Kann z.B. vom Resize Callback
       aufgerufen werden. Als Parameter werden die Breite und Hoehe des Fensters
       benoetigt. Berechnet daraus eine verzerrungsfreie Projektionsmatrix mit Zoom.
    */

    void setProjectionMatrix (int width, int height)
    {
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective (50.0*zoomFactor, (float)width/(float)height, zNear, zFar);
      /* 'zNear' und 'zFar' sind vorher festzulegen. */
    }

Man kann auch glFrustum() nutzen, allerdings ist die Berechnung der Parameter nicht immer ganz so einfach wie bei gluPerspective(), da sich die Parameter gegenseitig mehr beeinflussen. Vorausgesetzt, man behält einen konstanten Wert für zNear bei, könnte die Funktion so aussehen:

    glFrustum(left*zoomFactor, right*zoomFactor,
              bottom*zoomFactor, top*zoomFactor,
              zNear, zFar);

glOrtho() arbeitet vergleichbar.

8.050 Wie kann ich die aktuellen Koordinaten des Augpunktes nach einer Modelltransformation ermitteln ?

Die "Kamera" bzw. der Augpunkt liegt immer im Ursprung des Weltkoordinatensystems. Schreibt man diese Koordinate als Vektor [0 0 0 1] und multipliziert ihn mit der inversen ModelView Matrix, so erhält man den Augpunkt im Modellkoordinatensystem.

Da OpenGL die Inverse der ModelView Matrix nicht bereitstellt, muss man diese allerdings selbst berechnen.

8.060 Wie kann ich die Kamera um einen Punkt in meiner Szene kreisen lassen ?

Man kann einen kreisenden Orbit nachbilden, indem man die gesamte Szene um die feststehende Kamera rotieren lässt. Um z.B. ein Objekt um die Y-Achse des Weltkoordinatensystems kreisen zu lassen, kann man folgenden Code nutzen:

    gluLookAt(camera[0], camera[1], camera[2], /* von der Kamera bei XYZ beobachten */
              0, 0, 0,  /* auf den Ursprung schauen */
              0, 1, 0); /* der ViewUp Vektor liegt parallel zur Y-Achse */
    glRotatef(orbitWinkel, 0.0, 1.0, 0.0);/* um die Y-Achse drehen */
             /* orbitWinkel kann z.B. durch die Mausposition festgelegt werden */

    glCallList(SCENE); /* Szene zeichnen */

Um den gleichen Effekt mit einer tatsächlichen Kamerabewegung zu erreichen, muss der Ortsvektor der Kamera entsprechend geändert werden, bevor die Szene gezeichnet wird.

In jedem Fall wird die gluLookAt() Routine empfohlen.

8.070 Wie kann ich meinen Blickwinkel automatisch so festlegen, dass ich das gesamte Modell sehe ? (bekannt sind mir der umschliessende Kreis -Bounding Sphere- und der View Up Vektor)

Das folgende ist ein Posting von Dave Shreiner über das Festlegen eines Viewing Volumes:

Zunächst muss eine Bounding Sphere um alle Objekte der Szene berechnet werden. Als Ergebnis sollte der Mittelpunkt (hier als c.x, c.y, c.z bezeichnet) und der Durchmesser (hier:dm) bekannt sein.

Als nächstes wird ein Wert für zNear festgelegt. Dieser sollte etwas grösser als 1.0 sein. Damit ergibt sich:

    zNear = 1.0;
    zFar = zNear + dm;

Für eine Parallelprojektion werden folgende Aufrufe notwendig:

    GLdouble links  = c.x - dm;
    GLdouble rechts = c.x + dm;
    GLdouble unten  = c.y - dm;
    GLdouble oben   = c.y + dm;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(links, rechts, unten, oben, zNear, zFar);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

Dieses Vorgehen sollte die Objekte im Zentrum des Fensters plazieren und dabei fast die gesamte Fläche beanspruchen (bei einem Fenster mit Höhe/Breite = 1.0). Ist das Fenster nicht quadratisch, muss vor dem Aufruf von glOrtho() noch folgender Code eingefügt werden:

GLdouble aspekt = (GLdouble) breite / hoehe;

    if ( aspect < 1.0 ) { // hoehe groesser als breite
      bottom /= aspect;
      top /= aspect;
    }
    else {
      left *= aspect;
      right *= aspect;
    }

Dieser Code sollte die Objekte genau in das Fenster einpassen. Um jetzt eine Kamerabewegung entlang der Bounding Sphere zu realisieren, ist noch der folgende Aufruf vor anschliessenden Transformationen notwendig:

    GluLookAt (0., 0., 2.0 * dm,
               c.x, c.y, c.z,
               0.0, 1.0, 0.0);

8.080 Warum arbeitet gluLookAt() nicht ?

Die Ursache findet man normalerweise in fehlerhaften Transformationen.

Angenommen, es wird gluPerspective() auf dem Projection Stack mit zNear und zFar als dritten und vierten Parameter genutzt. Dann muss gluLookAt() auf den ModelView Stack angewendet werden, wobei die verwendeten Geometriedaten (Vertices) innerhalb von zNear und zFar liegen müssen (zumindest, wenn keine weiteren Transformationen durchgeführt werden). Am besten, man probiert zunächst mit einfachen Code, um die Zusammenhänge zu verstehen. Um beispielsweise auf eine Kugel (Durchmesser = 1.0, im Ursprung des Weltkoordinatensystems) zu beobachten, kann folgender Code verwendet werden:

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(50.0, 1.0, 3.0, 7.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 5.0,
              0.0, 0.0, 0.0,
              0.0, 1.0, 0.0);

Man sollte auf alle Fälle verstehen, wie der Projection und ModelView Matrix Stack zusammenarbeiten.

In diesem Beispiel wurde die Zentralprojektion mit einem Öffnungswinkel von 50 Grad und einem Höhe-Breite Verhältnis von 1.0 festgelegt. Die vordere Clippingfläche zNear befindet in einem Abstand von 3.0 vom Auge, die hintere zFar bei 7.0. Damit bleibt eine Tiefe von 4.0 des Viewing Volumens, also genug Platz für die Kugel.

Die ModelView Transformation positioniert das Auge auf der Z-Achse im Abstand von 5.0 mit Blickrichtung auf den Koordinatenursprung. Damit liegt das Zentrum des Viewing Volumens genau im Ursprung des Weltkoordinatensystems. Ein Abstand des Auges von 1.0 wäre nicht ausreichend, da die vordere Clippingfläche bereits hinter der Kugel, diese damit nicht mehr sichtbar wäre.

Der gleiche Effekt tritt auch bei einem Abstand von 10.0 auf, nur endet das Viewing Volume dann bereits vor der Kugel.

Weitere Informationen hierzu bietet das Red Book. Am besten ist es aber, einfach mit verschiedenen Werten und kleinen Testprogrammen zu experimentieren. Das ist auch der beste Weg, um darauf aufbauend komplexere Szenen zu realisieren.

8.090 Wie kann ich einen bestimmten Punkt meiner 3D-Welt genau in der Mitte der Szene plazieren ?

Den einfachsten Weg stellt gluLookAt() dar. Der vierte bis sechste Parameter ist der Punkt, auf den die Kamera gerichtet ist.

8.100 Ich rufe gluLookAt() im Modus GL_PROJECTION auf. Jetzt arbeiten Nebel, Licht und Texture Mapping nicht richtig. Was ist los ?

Dieses Problem wird in Frage 8.030 behandelt.

8.110 Wie kann ich eine Stereo-Ansicht erzeugen ?

Paul Bourke hat hierzu ein paar Informationen zusammengestellt:

Seite durchsuchen nach:

Fragen oder Ideen an:
Thomas.Kern@3Dsource.de
Linux-Counter
(C) Thomas Kern