Intro to VR Program Design

OK, you've written a cool interactive 3D program on your desktop PC (or even laptop) and, after watching Avatar, you've decided to convert it to stereoscopic 3D, "Virtual Reality". What do you need to do?

It can mean quite a lot of work, but not nearly as much as writing a 3D program in the first place. And once you've done it, you can reuse the code in future projects. Here are the steps:

I'm assuming you have some familiarity with 3D concepts such as projection and world matrices. I use OpenGL terminology to explain things, but everything here should be true for DirectX or whatever scene graph you decide to use.

Checking the program structure

The first step is to ensure you have separated out the per-frame code into these three blocks: (ideally, one function/method each)

By 'per-frame' I mean the event loop body, the event listener callbacks, the message loop and window procedure, whatever. Ideally it's happening once per frame.

The projection block selects the viewport or window to draw in, decides where the viewpoint or digital camera is, and establishes viewing parameters such as clip planes and field of view.

The render block renders the scene within the context established by the projection setup.

The update block swaps buffers and otherwise ensures that the scene is displayed to the user. Then it updates the 3D world state by responding to user input, cycling animations, applying gravity or particle system rules, and so on.

For a single window desktop 3D program this separation isn't necessary and it's easy to intermingle code. Before you can do anything else in VR, you have to get this right. Why?

The projection block must be separate because in VR we usually draw the same scene more than once with different projections. At minimum, the scene is rendered with two different projections per frame (one for each eye). In a CAVE fully immersive space there are twelve!

The render block must not reinitialise the projection, because then it would only work for one eye. It must not change the scene state, because it will have to be rendered again for the other eye/wall and the two scenes must be identical except for the projection. And it must not swap OpenGL buffers, because on a graphics card with more than one monitor all the buffers need to be swapped simultaneously.

The update block must be the only place where things are moved or otherwise changed, to ensure that the render block really does draw the same scene each time. Suppose you have a flock of birds flying around in your 3D world. It's very tempting to write a single render/update loop:

    for each bird:
        render it
	move forward by X

This will work fine in one window. But in stereo, because this code gets called twice, the left and right eyes will see the birds rendered in different positions, and they'll appear to be flying twice as fast. Instead the render block needs one loop which just renders, and the update block has another loop to move them.

Checking the structure doesn't require a stereo-capable PC and is easy to test. If you call the projection or render block twice within your frame loop, it should still look the same. (Possibly slower, but still looking identical.) If you call the update block more than once per frame, things should move faster.

Next: Redesigning for VR displays


Written by Hugh Fisher