Die Projektion eines Ausschnittes einer 3dimensionalen (virtuellen) Welt auf einen 2dimensionalen Bildschirm (auf die 3 Wände des Caves) erfolgt mit Hilfe eines Frustums (einer Pyramide mit abgeschnittener Spitze).
Die linke untere Ecke des Frustums hat also die Koordinaten (left/bottom/-near), die rechte obere Ecke (right/top/-near). Das Frustum kann somit durch Angabe dieser 2 Punkte und des Wertes far bestimmt werden.
Auf diese Weise kann natürlich auch ein nicht-symmetrisches Frustum erzeugt werden, so wie es im Cave benötigt wird (wenn sich der Betrachter nicht genau in der Mitte des Caves befindet)
Zunächst wird einmal der Viewpoint nach eyePos verschoben und die Blickrichtung normal auf die jeweilige Wand eingerichtet. Im Performer wird das etwa so aussehen:
pfChanView(channel, eyePos, dir), wobei dir einer dieser Vektoren sein könnte: (-90, 0, 0), (0, 0, 0), (90, 0, 0) (Drehung nach links, keine Drehung, Drehung nach rechts)
Jetzt müssen noch die Koordinaten von bottomLeft und topRight berechnet werden.
Sei d der Normalabstand von eyePos zur Cavewand.
seien die Ortsvektoren zu den jeweiligen Punkten.
sei der Vektor von eyePos nach A
sei der Vektor von eyePos nach C
Dann können die beiden Vektoren und ganz einfach berechnet werden:
Mit den Koordinaten von bottomLeft(left/bottom/-near) und topRight(right/top/-near) kann ein passendes Frustum erzeugt werden:
pfChanNearFar(channel, near, far)
pfMakePerspChan(channel, left, right, bottom, top)
Beispielcode, der die Werte für left, right, bottom und top berechnet.
void calculateFrustum(float eye[3], float cave[3], CAVE_WALL_ID wallID, float near, float frustum[4]) { float d, l, r, b, t; switch (wallID) { case CAVE_FRONT_WALL: d = 0.5 * cave[1] - eye[1]; l = 0.5 * cave[0] + eye[0]; r = 0.5 * cave[0] - eye[0]; b = 0.5 * cave[2] + eye[2]; t = 0.5 * cave[2] - eye[2]; frustum[0] = -l * near / d; frustum[1] = r * near / d; frustum[2] = -b * near / d; frustum[3] = t * near / d; break; case CAVE_LEFT_WALL: d = 0.5 * cave[0] + eye[0]; l = 0.5 * cave[1] + eye[1]; r = 0.5 * cave[1] - eye[1]; b = 0.5 * cave[2] + eye[2]; t = 0.5 * cave[2] - eye[2]; frustum[0] = -l * near / d; frustum[1] = r * near / d; frustum[2] = -b * near / d; frustum[3] = t * near / d; break; case CAVE_RIGHT_WALL: d = 0.5 * cave[0] - eye[0]; l = 0.5 * cave[1] - eye[1]; r = 0.5 * cave[1] + eye[1]; b = 0.5 * cave[2] + eye[2]; t = 0.5 * cave[2] - eye[2]; frustum[0] = -l * near / d; frustum[1] = r * near / d; frustum[2] = -b * near / d; frustum[3] = t * near / d; break; case CAVE_FLOOR_WALL: d = 0.5 * cave[2] + eye[2]; l = 0.5 * cave[0] + eye[0]; r = 0.5 * cave[0] - eye[0]; b = (0.5 * cave[1] - 60 ) + eye[1]; /* floor is 60cm shorter! */ t = 0.5 * cave[1] - eye[1]; frustum[0] = -l * near / d; frustum[1] = r * near / d; frustum[2] = -b * near / d; frustum[3] = t * near / d; break; } }