This report describes the steps involved in computing a geometrically accurate
projection transformation for a CAVE, ImmersaDesk, or IWall VR display (hereafter
referred to simply as a CAVE). The CAVE projection assumes a fixed rectangular display
screen that can be at any arbitrary position in space, with the viewer's eye able to move
anywhere in
front of the screen; see figure 1. To compute the projection, we are given the
positions of the corners of the screen - LL (lower left), UL (upper left),
and LR (lower right) - and the current tracked position of the viewer's eye.
(Note: since the screen is known to be a rectangle, only three
corner positions are needed.)
Figure 1. CAVE screen and eye-point in CAVE space
As the eye-point can be anywhere relative to the screen, the viewing volume is,
in general, an off-axis frustum. In OpenGL, we create the projection matrix
using glFrustum(). The glFrustum() matrix assumes an eye-point at the
origin, looking down the negative Z axis, with the projection plane parallel to
the X-Y plane (figure 2); hence, to complete the projection, we must also compute
a matrix which transforms the screen and eye-point from the situation of figure 1
to that of figure 2. This second transformation is loaded as the View matrix in
OpenGL.
Figure 2. glFrustum() off-axis viewing volume (screen space)
The "real-world" coordinate system of figure 1 will be referred to as CAVE space; the projection coordinate system of figure 2 will be referred to as screen space.
right = LR - LL width = || right || Xs = right / width up = UL - LL height = || up || Ys = up / height Zs = Xs x YsGiven these axes, we can compute the rotation portion of the view matrix for the CAVE-to-screen-space transformation. Since (Xs,Ys,Zs) are the screen-space coordinate axes in CAVE-space, they define a transformation from screen- to CAVE-space:
| Xs[0] Ys[0] Zs[0] | | Xs[1] Ys[1] Zs[1] | | Xs[2] Ys[2] Zs[2] |The desired transformation is then just the inverse of this matrix.
| Xs[0] Ys[0] Zs[0] | -1 RotMat= | Xs[1] Ys[1] Zs[1] | | Xs[2] Ys[2] Zs[2] |
As shown in figure 2, the value of L is the distance from the eye-point to the left edge of the screen, along the Xs axis. Similarly, B is the distance from the eye-point to the bottom edge of the screen, along the Ys axis. Therefore, these values can be computed by taking the dot product of the screen axes with the eye position relative to the lower-left screen corner. R and T can then be computed using the width and height of the screen.
eyes = eye - LL L = eyesThe left/right/bottom/top arguments for glFrustum() must define the corners of the near clipping plane. Using similar triangles, we compute these values from L/R/B/T, scaling them by the ratio of the near clipping distance to the distance between the eye-point and the screen:Xs R = width - L B = eyes
Ys T = height - B
distance = eyesZs left = -L * near / distance right = R * near / distance bottom = -B * near / distance top = T * near / distance
ViewMat = translate(-eye[0],-eye[1],-eye[2]) * RotMat