Fun with voxels

2012-05-15

I've been working on a new game lately, with a nice pseudo-3D effect for the display. After a while, it dawned on me that what I was doing there essentially amounted to voxels. Which was strange, because while I had read about voxels before, my interest in the topic was academic at best. But now the connection was made, I decided to take a closer look, just to know what possibilities I might be overlooking.

But first, what exactly are voxels?

You know what pixels are, right? On the computer, an ordinary two- dimensional image is made of little dots arranged in a grid. Whenever you want to see what the image represents, you simply draw each dot on the screen, row after row, making sure the dots in each row line up vertically. Well, voxels are exactly the same thing, except in 3D.

pixels versus voxels

Which brings up an obvious question: short of an actual volumetric display, how do you present a three-dimensional grid of dots to a viewer? Why, via 3D projection of course! And as I demonstrated in a previous article, that's really easy to do for the basic case.

	var TwoPointFive = {
		Camera: function (x, y, z, f) {
			this.x = x || 0;
			this.y = y || 0;
			this.z = z || 0;
			this.f = f || 200;
		},
		
		project: function (x, y, z, camera) {
			x -= camera.x;
			y -= camera.y;
			z -= camera.z;
			var scale = camera.f / z;
			var px = x * scale;
			var py = y * scale;
			return [px, py, scale];
		}
	}

With that in place, all we have to do is go over each layer in turn and plot the coordinates of each voxel on screen.

	for (var z = DEPTH - 1; z >=0; z++) {
		for (var x = 0; x < WIDTH; x++) {
			for (var y = 0; y < HEIGHT; y++) {
				var p = TwoPointFive.project(x, y, z, camera);
				// Determine the voxel's color somehow.
				var color = getColor(x, y, z);
				// Paint your voxel here.
			}
		}
	}

Here, I make sure to start from the back and come towards the camera, a.k.a. painter's algorithm. A more popular option nowadays is to generate a cube around each voxel and let OpenGL worry about the rendering; but then, that would take care of the 3D projection as well. As it is, I simply paint each voxel as a square, scaled according to distance from the camera.

voxel tux

Wait... isn't it cheating to use a flat image in a discussion of volumetric pixels? That's more like... a texture! No, really, it is. It's also the same basic principle; with voxels, we simply have layer after layer of data, instead of just one. Which presents a problem.

voxel pyramids

Believe it or not, this coarse scene contains as many voxels as the one before. (Though mostly obtained by mirroring.) The square-cube law ensures that the amount of data required increases fast with resolution. Luckily, there are ways to mitigate this problem.

voxel heightmap

This is an ordinary heightmap, except rendered with voxels instead of polygons. The height of each data point is encoded in its shade of gray. It works because we're only interested in the surface -- and as a bonus, we can still use the same value to color the voxel. (Under realistic conditions, a color map would be in order.) Note how I've drawn the voxels taller than they are wide in order to hide the seams, and it's still not perfect. I guess there's a reason polygons are more popular.

What's the point of using voxels, then? Nowadays, they are mostly used as a stylistic choice, as they give games a very distinctive appearance. They also naturally enable fully modifiable environments, something rather difficult to implement in a polygonal world. Otherwise, polygons are better studied and directly supported by the commodity GPUs built into almost any computer.

Back around 1997, however, before 3D acceleration was ubiquitous, voxels were enjoying considerable attention. Quite a few famous games used voxels for some or all of their graphics, Blade Runner (1997) being my favorite example. See, voxels are easy to understand, and require very simple calculations. But the comparative difficulty of animating voxel objects, together with the march of technology, relegated them to just another tool in the toolbox. Use wisely.

Tags: