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:
|