Retro Renderer

Retro Renderer is a 3D rendering engine that produces images that look like they were drawn by pixel artists. The images could then be used in a computer game. Here's a wizard for an RPG.

It accepts any object in Wavefront's .obj format as input.

I wrote it over spring break in 2010. There's a thread about it on here.


Retro Renderer is available in compiled form as a Windows console application.

Download Windows Executable

Retro Renderer's source consists of about 2,000 lines of C++ source code. It should run on any platform that supports GCC. It doesn't require any libraries other than the standard libraries.

Download Source


See original

Download Model (.obj)

Download Materials (.mtl)

This model is made by my brother, Scott Maxwell.


Some people have asked for technical details of how Retro Renderer works. Most of the techniques that Retro Renderer uses are industry-standard, and I will not describe those. From here on, I will assume that you know how a basic 3D renderer works.


Retro Renderer supersamples its images at three times the requested resolution. This is to eliminate ugly artifacts at the edges of polygons. At the low resolutions that Retro Renderer works at, eliminating these artifacts is very important.

In the supersampling phase, Retro Renderer renders fragments into three buffers: a depth buffer, a normal buffer, and a material buffer. After rasterizing all of the faces, each three-by-three group of pixels is examined to determine which material appears most often in the group. The material of the group as a whole becomes whichever material was most common within the group, and then rendering proceeds as though the entire group had that material.

This approach is chosen instead of the normal color-blending approach in order to make the images look more "retro". If the image were supersampled by scaling it down after rendering it, it wouldn't look like pixel art.

Banded lighting

After determining the material, average normal, and average depth for each three-by-three group, Retro Renderer performs lighting calculations. Lighting calculations are done using the ordinary Phong shading model with a minor modification: after computing the dot product of the surface normal and the light vector, the renderer rounds this number to produce a "banded" shading effect. Specifically, the code is:

double bias = 0.2;

double alignment = (dot(light.direction, normal) + bias) / (1 + bias);

if (alignment > 0)
	// Make shading granular
	if (alignment < 0.6) alignment = 0.4;
	else alignment = 0.8;
	// Diffuse lighting
	color = color + alignment * mat.diffuse * light.color;
	// --- snip specular lighting code here ---

Drawing outlines

The final step is to draw the black edges on the image. Retro Renderer paints a pixel black if:

  • At least one of the pixel's neighbors is painted with a different material than the pixel itself; and
  • The pixel has a greater depth value than the aforementioned neighbor.

This technique works better than a Sobel filter because it does not draw lines at the boundaries between shaded and non-shaded areas of the same object, and because it can draw boundaries between two different objects of the same color if they are painted with different materials.

However, it causes problems sometimes! If the model artist uses the same material for two distinct objects in the scene, then a boundary will not be drawn between them. Look more closely at the knight's boots:

Because I used the same texture for the knight's two boots, the renderer did not draw a line between them. Oops... The solution would be to make an identical texture with a different name, and color one boot in each texture.

Future directions

At some point I might add shadow-casting to Retro Renderer.


Retro Renderer's source code is released under the MIT license.

Regarding the images created with Retro Renderer: As far as I'm concerned, you own the copyright on any images you create with Retro Renderer, and so you can use them for anything you like, including commercial purposes. (Sorry I don't speak legalese. If you need a more official declaration, contact me and we can work something out.)