COMP 238 Final Project: Non-Photorealistic Rendering Techniques for a Game Engine

Goal

For this project, I set out to implement a few non-photorealistic rendering (NPR) techniques in a real-time rendering framework: a game engine.
I chose NPR Quake because it already had a structure I could use. The source of the Quake 2 engine is also available under GPL on the ID Software web site, and its structure is fairly similar, but I would have needed to reproduce the work already done for NPR Quake: pull out the rendering calls and make a framework for loading dynamic rendering libraries. I wanted a cartoon style throughout, but given the framework of NPR Quake, only part of it was possible.

Techniques

Silhouettes for animated objects

I implemented the silhouettes rendering technique described in Ramesh Raskar's Hardware Support for NPR.
In brief, the technique is to render the backfacing polygons enlarged. Since front polygons cover them, only the enlarged parts show through, resulting in the desired silhouettes.

Three variables control the rendering mode (type as commands in the console to experiment):
AINPR_SIL_MODE -1..3 (-1=NONE, 0=FLAT, 1=SHADED, 2=TEXTURE, 3=TOON, default 0)
AINPR_SIL_COLOR 0xRRGGBB (color is specified as a hex number, default 0x808080)
AINPR_SIL_WIDTH w (silhouette width in pixels, default 3)

Cartoon style rendering for animated objects

I implemented toon style rendering using the technique described in Shades of Disney: Opaquing a 3D World and Stylized Rendering Techniques for Scalable Real-Time 3D Animation.
In brief, the technique is to use a 1D texture with 3 shades: shadow, normal and highlighted, and set each vertex's texture coordinate to a number proportional to the total intensity of light it receives. After disabling linear filtering, the result is the desired cartoon style rendering. Each object has its own color (set in the file "colors.txt").

One variable controls the rendering mode (type as commands in the console to experiment):
AINPR_BODY_MODE 0..3 (0=FLAT, 1=SHADED, 2=TEXTURE, 3=TOON, default 3)
Two other variables control the shading:
AINPR_SHADE_LO 0..1 (threshold between shadow and normal, default 0.2)
AINPR_SHADE_HI 0..1 (threshold between normal and highlighted, default 0.6)

Simulated cartoon style for walls

The models of animated objects have per-vertex lighting information (pre-computed light intensity, instead of normals). This information is not available for walls. The final look in the game is achieved by blending lightmaps with the texures. To achieve a cartoon style rendering, I chose a texture that looks hand painted, and I rendered the polygon boundaries in a style similar to the one of the "sketch" NPR Quake renderer (one thick line and several blended and jittered thin lines). Alternative textures can also be chosen.

Three variables control the rendering mode for regular walls (type in as commands in the console to experiment):
AINPR_WALL_MODE 0..4 (0=FLAT, 1=SHADED, 2=TEXTURE, 3=TOON, 4=SKETCH, default 4)
AINPR_LINE_MODE -1..1 (-1=NONE, 0=FLAT, 1=TEXTURE, default 0)
AINPR_LINE_WIDTH w (line width in pixels, default 3)
Three more variables control the rendering mode for underwater walls (type in as commands in the console to experiment):
AINPR_WALL2_MODE 0..4 (0=FLAT, 1=SHADED, 2=TEXTURE, 3=TOON, 4=SKETCH, default 4)
AINPR_LINE2_MODE -1..1 (-1=NONE, 0=FLAT, 1=TEXTURE, default 0)
AINPR_LINE2_WIDTH w (line width in pixels, default 3)
The wall texture is set by:
AINPR_TEX_NO 0..31 (texture number, default 31)
The blending of charcoal style lines is controlled by:
AINPR_ALPHA 0..1 (alpha value, default 0.25)
The number of additional charcoal lines drawn for normal walls:
AINPR_WALLS n (number of lines, default 6)
The number of additional charcoal lines drawn for underwater walls:
AINPR_WATER n (number of lines, default 6)

Creases

I implemented the creases rendering technique described in Ramesh Raskar's Hardware Support for NPR.
In brief, the technique is to render thin quadrilaterals at a specified angle with respect to a polygon for each of the polygon's edges. If the angle s the polygon makes with the neighboring polygons are sharper, the quadrilaterals will be visible, resulting in creases. Unfortunately, as seen below, there are several problems with the implementation. First, the models are too coarsely tesselated to make creases show any interesting features. Second, to make the game fast, the resolution of the Z buffer is low, so stitching occurs because of Z fighting. Third, a bug in the NPR Quake implementation draws an extra line for some models. This is why I decided to turn off creases by default, but leave the code in for further study.

Three variables control the rendering mode (type as commands in the console to experiment):
AINPR_CREASE_MODE -1,0 (-1=NONE, 0=FLAT, default -1)
AINPR_CREASE_COLOR 0xRRGGBB (color is specified as a hex number, default 0xFFFFFF)
AINPR_CREASE_WIDTH w (crease width in pixels, default 1)
AINPR_CREASE_ANGLE 0..180 (angle in degrees, default 120)


Shadows for animated objects

There is a bug in NPR Quake: the function GL_DrawAliasShadow is never called. That's why I put in a pseudo-shadow rendering (illustrated below) similar to the one in the "sketch" NPR Quake renderer.
In brief, the technique is to render all the model's polygons in black, at a constant height. This is obviously wrong if the floor is not horizontal, and does not take into account the lights, but it's the best substitute for the function that is never called.

Three variables control the shadow rendering mode (type as commands in the console to experiment):
AINPR_SHADOW_MODE -1,0 (-1=NONE, 0=FLAT, default 0)
AINPR_SHADOW_HEIGHT h (the height at which the shadow is displayed, default 10)


Future directions

Using several passes, the cartoon style look can be achieved for the walls. However, the resolution of the lightmaps is too coarse, and the results will not look nearly as good as the moving objects.
The Z-buffer resolution may be increased to reduce Z fighting when rendering creases. Also, creases can be rendered as trapezes instead of rectangles, to make sure their corners do not poke through the model.
Other methods exist for silhouettes, that modify the vertices of backfacing polygons instead of just modifying the line width. They yield more pleasant results at the corners for large silhouette widths.
Also, there seems to be yet another bug in the way NPR Quake loads its textures that makes it overwrite the IDs of some of the standard textures: going back to "dr_default" after loading many textures in "ainpr" may overwrite the textures used of the menu and/or the walls. While the game is still usable, this bug is annoying enough to warrant further investigation.

Results

Below are some screenshots produced by my implementation:

Links

Update: This renderer has been added to Jeremy Green's Linux port of NPRQuake. It also got some coverage from CrazyHacks and The Linux Documentation Project.

Last modified on Monday, August 11, 2008 by .