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*.

Given these axes, we can compute the rotation portion of the view matrix for the CAVE-to-screen-space transformation. Since (right=LR-LLwidth = ||right||X=_{s}right/ widthup=UL-LLheight = ||up||Y=_{s}up/ heightZ=_{s}Xx_{s}Y_{s}

|The desired transformation is then just the inverse of this matrix.X[0]_{s}Y[0]_{s}Z[0] | |_{s}X[1]_{s}Y[1]_{s}Z[1] | |_{s}X[2]_{s}Y[2]_{s}Z[2] |_{s}

|X[0]_{s}Y[0]_{s}Z[0] |_{s}^{-1}RotMat= |X[1]_{s}Y[1]_{s}Z[1] | |_{s}X[2]_{s}Y[2]_{s}Z[2] |_{s}

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 **X _{s}** axis.
Similarly, B is the distance from the eye-point to the bottom edge of
the screen, along the

The 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:eye=_{s}eye-LLL =eye_{s}XR = width - L B =_{s}eye_{s}YT = height - B_{s}

distance =eye_{s}Zleft = -L * near / distance right = R * near / distance bottom = -B * near / distance top = T * near / distance_{s}

ViewMat=translate(-eye[0],-eye[1],-eye[2]) *RotMat