Drawing multiple viewports at the same time

24 January 2015

Sometimes in graphical applications you want to draw multiple viewports on the same screen at the same time. Think about split-screen games, CAD programs showing multiple views, or a map overlay. Approaching this problem recently, I was surprised by the dearth of information on the subject.

While this can be achieved through the use of framebuffers – render each viewport to a framebuffer, then draw each onto the screen – it can be also be done in only one rendering pass. After all, since we use projection matrices to map a camera’s view frustum into screen-space, we can obviously use a matrix to render to only a part of screen-space. How exactly these matrices are constructed wasn’t turning up in any searches or graphics books that I looked at, so I ended up deriving them myself. Songho’s excellent guide to projection matrices explains how projection matrices are derived, so I won’t go into detail into how I arrived at my results. The only thing I did differently from Songho is instead of mapping $$x$$ and $$y$$ to $$[-1, 1]$$ (the boundaries of screen-space) you map them to an arbitrary $$[x_{left}, x_{right}]$$ and $$[y_{bottom}, y_{top}]$$ where $$-1 \le x_{left} \le x_{right} \le 1$$ and $$-1 \le y_{bottom} \le y_{top} \le 1$$. With a view frustum with a near plane defined by the points $$(l, t, n)$$, $$(r, t, n)$$, $$(l, b, n)$$, $$(r, b, n)$$, and a far plane at $$z = f$$, the resulting projection matrices look like this:

Perspective

\begin{pmatrix} \frac{n(x_{right} - x_{left})}{r - l} & 0 & \frac{lx_{right} - rx_{left}}{r -l} & 0 \\ 0 & \frac{n(y_{top} - y_{bottom})}{t - b} & \frac{by_{top} - ty_{bottom}}{t -b} & 0 \\ 0 & 0 & \frac{-f - n}{f-n} & \frac{-2fn}{f-n} \\ 0 & 0 & -1 & 0 \end{pmatrix}

Orthographic

\begin{pmatrix} \frac{x_{right} - x_{left}}{r - l} & 0 & 0 & \frac{rx_{left} - lx_{right}}{r - l} \\ 0 & \frac{y_{top} - y_{bottom}}{t - b} & 0 & \frac{ty_{bottom} - by_{top}}{t - b} \\ 0 & 0 & \frac{-2}{f-n} & \frac{-f+n}{f-n} \\ 0 & 0 & 0 & 1 \end{pmatrix}

These matrices made it into Hypermath as the functions hpmFrustumViewport and hpmOrthoViewport which found their way into Hyperscene and consequentially it’s now easy to make split-screen views in Hypergiant1:

Note that splitting the viewport like this should typically be used with glScissor (or something equivalent, if you’re not using OpenGL), since these matrices are simply providing a different mapping of a viewport into screen-space and are not doing any clipping themselves. If, for example, you want to render to the left side of the screen, there’s nothing inherent in the projection matrix that will cause things falling into the right side of the screen – which is still a valid portion of screen-space – to be clipped. So to achieve this, we have to employ glScissor as well. In the above image, you can see how a portion of the small cube on the right side is being clipped.

1. Check out the split-screen example to see the program that created that image.