Order-of-Operations
and the
"Quake 3: Arena" Graphics Engine

by Bryan McNett

In July 1998, John Carmack announced further details regarding the Quake 3 : Arena (Q3A) graphics engine. This article attempts to explain recent changes to the Q3A graphics engine while exploring order-of-operations. After you're done, you may download all-new files that enable you to play with the new features of the Q3A graphics engine in real time using Adobe Photoshop.

note! the web server was corrupting the photoshop files. this was fixed at 10am on 7/27. we apologize for the inconvenience.

This article assumes you have read Multitexture and the Quake 3 Graphics Engine (MATQ3GE).

Most relevant to our ongoing discussion of multitexture, bump-mapping has been dropped from Q3A's list of features due to excessive hardware requirements. This removes bump-mapping from the equation given in MATQ3GE. As a subtle side effect, the light-map is now free to appear later in the equation. It may be unclear what this means or why this would be important. It may help to discuss order-of-operations. Let's start with ordinary arithmetic:

1+2*3

Because multiplication and division are always done before addition and subtraction, two and three are multiplied together, and then one is added to produce seven. But parentheses can be used to rearrange the order-of-operations:

(1+2)*3

In this case, one and two are added, and then the result is multiplied by three to produce nine.

Multitexture can be done with alpha blending, multiple texture units, or a combination of both. Only the best 3D cards have multiple texture units, so we will concentrate on techniques that require only alpha blending.

Because alpha blending means blending a polygon with what's already on the screen, each operation is applied to the grand total of all previous operations. This order-of-operations doesn't care about multiplication coming before addition - it moves strictly from the start of the equation to the end, step by step. It is impossible to reorder the operations with well-chosen parentheses.

You can't choose the parentheses, but you can avoid confusion by wrapping everything in parentheses like so:

(((((A+B)*C)+D)*E)+F)

In MATQ3GE, many layers of bump-mapping are added to one layer of light-mapping. The result is multiplied with texture-mapping.

(((((L+B1)+B2)+B3)+B4)*T)

If the light-map and bump-maps were applied after the texture-map, the operations would not be done in the same order, and the result would not look the same, to say the least:

(((((T*B4)+B3)+B2)+B1)+L)

But if the bump-maps are gone (as is the case with Q3A), the result is the same regardless of the order-of-operations:

1. (L*T)

2. (T*L)

The light-map is free to come after the texture-map. Now that we've explained what this means, let's explain why it's important.

Let's consider the specular-map. Its purpose is to add shiny highlights to the texture-map. We can enable the specular-map in the above two equations in one of two ways:

1. ((L*T)+S)

2. ((T+S)*L)

The first equation shows us how the specular map was applied in MATQ3GE - by necessity, only after the light-map had already applied shadows to the texture-map. The second equation shows us how, without bump-mapping, the specular-map can be applied before shadows are applied.


click here to download bad.psd

1. ((L*T)+S)

when specular-mapping is applied after light-mapping, the shiny parts "glow" when they enter shadow. this looks bad.

click here to download good.psd

2. ((T+S)*L)

when specular-mapping is applied before light-mapping, the shiny parts are obscured by shadow. this looks good.



This is important because shadows are supposed to be applied after specular highlights. If the specular-map were to shine through shadows (as was the case in MATQ3GE), they would be specular in bright light, but emissive in shadow (note to Quakers: emissive is just a fancy way of saying fullbright). It would not be easy to design specular-maps that look good under these conditions.

For what it's worth, the above problem was contrived to help explain the kind of problems that arise with order-of-operations. Many solutions do not involve tossing away bump-mapping.

It is possible to get correct shadows and bump-mapping by combining bump-maps and specular-maps into specular-bump-maps:

(((((T+S1)+S2)+S3)+S4)*L)


click here to download specular-bump.psd

specular-bump-mapping kills two birds with one stone.

It is even possible, with the help of palettes, to combine all four specular-bump-maps into a single super-specular-bump-map:

((T+S1234)*L)

Finally, the most twisted trick of all: the four specular-bump-maps for each 16 bit texture-map are packed into the same two bits of four separate 8 bit textures. Each 8 bit texture is shared by the specular-bump-maps of four 16 bit texture-maps. A palette selects which two bits define a four-color grayscale. If polygons are sorted by palette, only four palette switches are needed per frame. Like usual, six passes are required to get texture-mapping, specular-bump-mapping and light-mapping, but texture memory requirements drop from 48 bits-per-pixel to 24!