Sunday, October 4, 2020

Stereoscopic computing: converting Quake and Doom

This article is part of a series -- you can read other entries

In our first 3D article, we talked about the basics of stereoscopic computing, including anaglyph rendering and active versus passive displays, and demonstrated a tool for turning a twinned set of webcams into a 3D image.

Games, of course, are the most obvious application, but early 3D games mostly dealt with the phenomenon as overlapping 2D planes due to the lack of 3D acceleration, limited CPU power and the predominance of 2D sprite hardware in contemporary video chips. These almost all used anaglyph colours (red/cyan glasses and others), but a few exploited the Pulfrich effect, and we'll look at some examples of these with the Commodore 64 in a later post. Even true stereoscopic systems like the Virtual Boy still largely used scaled planar images rather than true 3D graphics even though their separated video pathways yielded much better results (and eyestrain). As our previous article discussed, we try to avoid anaglyph rendering because all of them have some level of colour distortion (because they use colour differences to deliver a different picture to each eye) and ghosting (because that process is imperfect), and Pulfrich rendering, for reasons we will discuss, has only limited applications. For these examples, we will continue with my trusty Mitsubishi Diamondcrysta RDT233WX-3D, a passive polarized 3D display, though this should work for any passive 3D monitor or television. The general principles also apply to active systems like Nvidia 3DVision, though the details will be peculiar to each particular vendor-proprietary system.

One of the earliest games to have a true 3D mode built into it was id Software's Quake, and, perfect for alternating line polarized passive displays, it interlaced the left and right eye views. However, it was actually created for active glasses alternating between left and right views in sync with the alternating scans of a CRT monitor (here is one such scheme), though this method can still be simulated with a line blanker with modern display cards and LCD panels. This code persisted into early source ports of Quake when id released the code under the GPL, such as in the software-rendered SDLQuake, and is enabled by going to the console and entering lcd_x 1 (the higher the value, the greater the stereo separation). SDLQuake can still, with some effort, be built on modern systems but is strictly limited to 640x480. Naturally, interlaced images will not appear in 3D on a 2D monitor, and may need to be downloaded and moved to the right vertical position to match the polarization order of your screen. For reasons that will become clear in a moment, these were captured during demo playback and could not be paused, but they still serve for purposes of comparison even though the scenes are not exactly identical.

lcd_x 0 (default, 2D only)

lcd_x 1

lcd_x 5

The highest stereo separation yields great effects for most of the display but makes the weapon uncomfortably close, which even without 3D glasses you can tell by how widely spaced the left and right views of it are. For my money the game is really only playable at the lowest separation setting, except that on modern 64-bit systems, SDLQuake and other early releases are not playable at all; they crash immediately when trying to start a new game because they are not 64-bit aware.

Modern Quake ports have corrected this problem but in turn have eliminated much, if not all, of the original stereoscopy support. A few have secondarily added back 3D using alternative means. One such port is Quakespasm, a continuation of the older Fitzquake, which in turn descends from id's GLQuake. Unfortunately, it only has an anaglyph mode (set r_stereo to a non-zero value in the console) and the old interlaced mode is gone. Let's fix that!

Pure software rendering would deal with this problem by taking two images and going to bottom copying one view to odd lines and the other to even lines. A faster approach on systems with SIMD is to blit over one screen entirely using those nice fat vector registers and then line in the other appropriately, which is the approach we took in Camaglyph. However, this is using OpenGL, so we have an additional option of using the stencil buffer. A stencil buffer approach would draw alternating lines in the stencil buffer and then map the left view and right view depending on whether there's a line in the stencil buffer or not. GLQuake does not itself make use of the stencil buffer (too early) and Quakespasm only uses it to prevent intersecting shadows, so we can easily remove that small amount of code to have exclusive control of the stencil buffer since the shadow intersection code is infrequently of rendering relevance.

I don't claim this to be the best way of accomplishing it, but here's my patch against current versions. You will, of course, have to build it yourself from the source code. To enable it, go into the console (usually the tilde key) to set the appropriate options. r_stereo, same here as in regular Quakespasm, sets the stereo separation. I wouldn't make this much higher than 2.0; even that gave me a little bit of a headache, but since it's helpfully a float value 1.5 (in the console, type r_stereo 1.5) is a nice compromise since 1.0 doesn't really have enough depth. To enable interlacing, set the new cvar r_stereomode to either 2 (L-R) or 3 (R-L: my Diamondcrysta is R-L), or set it to 1 for the old red-cyan anaglyph. Because it allows larger screen displays the 3D-enabled Quakespasm is much more immersive and is really quite fun to play. For this screenshot I set the separation to a value of 2.0 so that the effect is a little more exaggerated.

This should work on any system (Mac, Windows or Linux, which is my usual environment) and on any graphics card of vaguely recent vintage.

I mentioned Doom in the title, which is more "2.5D," and mixes flat sprites (characters, objects) with 3D environments. There are many modern ports of this but the one I've come to recently enjoy is GZDoom, which as a built-in feature now has row interlacing as well as many other 3D modes in current releases of its renderer. To do this, pull down the console (tilde key) and enter vr_mode 12. Row interleaving in GZDoom is L-R, so I also add vr_swap_eyes true to make it R-L. You can also add this to gzdoom.ini wherever your system stores it (on Linux, ~/.config/gzdoom/gzdoom.ini). Because it tries to centre the window by default, however, I made a tiny patch to force everything to the 3D monitor which I use as a second, spanned display.

In future articles we will explore 8-bit ways of achieving 3D, and consider an alternate approach for when you want to convert a game to stereoscopic 3D but it's already using the stencil buffer.

No comments:

Post a Comment