COMP 238 Final Project: Non-Photorealistic Rendering Techniques for a
For this project, I set out to implement a few non-photorealistic
rendering (NPR) techniques in a real-time rendering framework: a game
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.
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
AINPR_SIL_MODE -1..3 (-1=NONE, 0=FLAT, 1=SHADED, 2=TEXTURE, 3=TOON,
AINPR_SIL_COLOR 0xRRGGBB (color is specified as a hex number, default
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
AINPR_BODY_MODE 0..3 (0=FLAT, 1=SHADED, 2=TEXTURE, 3=TOON, default
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
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
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,
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,
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)
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
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
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
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
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.
Below are some screenshots produced by my implementation: