Multitexturing in the Unreal Engine

Unreal and multitexturing

During the development of the Unreal Engine Direct3D 10 renderer I noticed that the 'UseMultiTexture' setting for the D3D and OpenGL renderers in some cases resulted in drastically differently looking skies; reflections looked different as well.

Multitexturing (or single-pass multitexturing) is the ability of a video card to blend two textures onto a pixel at the same time, without having to draw geometry twice. When multitexturing is not supported, multi-pass rendering is used instead, i.e. the geometry is drawn multiple times with different textures, and these results are blended. The Unreal engine uses this to draw diffuse textures and lightmaps in a single pass.

The D3D10 renderer always uses multitexturing, but, by using some blend mode tricks, can enable the look of multi-pass rendering; this is what its 'Simulate multi-pass texturing' option does. I decided to enable it by default, assuming this is what the games were supposed to look like. Looking at the Dark Arena screenshots above, it's clear what the designers meant the sky to look like, right?

This is the Steam version of Unreal Gold. Dark Arena's skybox without multitexturing (as faked by the D3D10 renderer). This matches the standard D3D and OpenGL renderers with 'UseMultiTexture' set to false.
Dark Arena's sky box, again under D3D10 but this time with multitexturing. This matches the standard D3D and OpenGL renderers with 'UseMultiTexture' set to true (which is the default). Note that Dark Arena is the only map I've found where the difference in look is this pronounced.

Unreal and Glide

The original version of Unreal only supported two renderers: software rendering and 3dfx Glide. Back in 1998 when Unreal was released, 3dfx was the standard as far as gaming 3D acceleration was concerned, so it makes sense that Glide the first API the game supported; Direct3D and OpenGL support followed, and were improved, in patches. The different look of Dark Arena led me to conclude that Unreal had not used multitexturing originally, I as such concluded that the Glide renderer must not support multitexturing. This was confirmed when I tested with a Glide wrapper. Furthermore, the original 3dfx Voodoo graphics card did not support multitexturing at all. However, I was surprised to hear that someone had checked Dark Arena's skies on a real 3dfx card (no idea of the model) and found them to look as with multitexturing enabled.

I decided to put together an old computer with a Voodoo 3 3000 card to check things out for myself. Believe it or not, Dark Arena actually had the drab, white sky. On a hunch, I tried the very first version of the original non-Gold Unreal, and this did have the colored skies - apparently some patch introduced multitexturing! The patch notes for the 118 patch for the game state:

Improved Glide support: Voodoo2 Dual-TMU, more stable Voodoo Rush & Banshee support. Thanks to Jack Mathews @ 3dfx for the engineering help!

A TMU is a Texture Mapping Unit. Note that the original version of Unreal does detect multiple TMUs:

Init: Initializing Glide...
Init: Found Glide:
Init: Glide info: Type=0, fbRam=12 fbiRev=69634 nTexelfx=2 Sli=0
Init: Glide tmu 0: tmuRev=69634 tmuRam=2 Space=2097136
Init: Glide tmu 1: tmuRev=69634 tmuRam=2 Space=2097136
Log: Flushed Glide TMU 0
Log: Flushed Glide TMU 1

Interestingly, Unreal 118 still had the colored skybox on my Voodoo 3, though it also introduced various glitches such as weird color blobs on textures and garbled text. All versions I tried between 216 and 224 either crashed or gave me graphics gliches, but the versions that worked did have the colored skybox. The release notes for the 220 patch, which wasn't public but its release notes come with the 224 patch, state:

Works properly on Voodoo3. Voodoo3 support was broken in Unreal 209 through 220.

Which explains why v224 works perfectly, but it's also the first (working) version that results in the grey skybox!

Dark Arena's skies on a Voodoo 3 card running the original version of Unreal. Don't mind the dark gamma, unfortunately gamma correction doesn't show up in Glide screenshots.
Again the Voodoo 3, this time on Unreal 224.

Glide wrappers

Although it's clear the Unreal Glide renderer introduced multitexturing support at some point, that doesn't change the fact that the original Voodoo Graphics card just plain doesn't support it. Unfortunately I didn't have one of those to test with, but thankfully Zeckensack's Glide wrapper presents itself as an original Voodoo Graphics card. Predictably, Dark Arena preserved the colored skybox for all versions of Unreal, up to and including Unreal Gold:

Dark Arena running on Zeckensack's Glide wrapper and Unreal Gold.

I also tested nGlide. nGlide matched my Voodoo 3 card, though I'm not sure what kind of card it presents itself as and/or how the Unreal Glide renderer detects it. It too switched to the white skies with v224; perhaps this wrapper is detected as a Voodoo 3 (or later) and v118's 'Dual-TMU' support is only activated when the renderer detects a Voodoo 2 card. dgVoodoo matched Zeckensack, i.e. Voodoo 1 style.

More Unreal comparisons

Harobed Village, Voodoo 3, original version of Unreal.
Harobed Village, Voodoo 3, Unreal v224.
Harobed Village, D3D10 SimulateMultiPassTexturing=true, Unreal Gold.
Harobed Village, D3D10 SimulateMultiPassTexturing=false, Unreal Gold.
Nyleve's Falls, Voodoo 3, original version of Unreal.
Nyleve's Falls, Voodoo 3, Unreal v224.
Nyleve's Falls, D3D10 'Simulate multi-pass texturing'=true, Unreal Gold.
Nyleve's Falls, D3D10 'Simulate multi-pass texturing'=false, Unreal Gold.

Unreal Tournament GOTY Edition

From the Unreal screenshots we can conclude that the game was developed for cards with no multitexturing support, which was introduced in a later version of the game. This makes looking at Unreal Tournament interesting; it's safe to assume its developers were using cards that supported multitexturing as those were the norm at the time of its development.

The Pit of Agony on the Voodoo 3.
The Pit of Agony, D3D10 'Simulate multi-pass texturing'=false.
The Pit of Agony, D3D10 'Simulate multi-pass texturing'=true. Looks much better, doesn't it? This applies for many UT maps.
Here's the kicker, the map selection preview image clearly matches the multitexturing scenario. This confirms that the developers were using multitexturing. This applies to all maps I checked.

Deus Ex GOTY Edition

For Deus Ex there isn't any difference in its (simplistic) skyboxes; the most important difference is in how reflections are treated. Generally disabling multitexturing makes the reflections look better, but not always. It's likely that the reflection code was unchanged since the original version of Unreal and, as such, designed for cards without multitexturing support. However, just like UT, the actual maps were probably designed by developers using cards with multitexturing enabled.

The intro scene running on the Voodoo 3.
Intro scene on D3D10 with SimulateMultipassTexturing=true. Note how the hand's shadow is more pronounced, but there are also some glitches visible.
Intro scene on D3D10 with SimulateMultipassTexturing=false.
Intro scene on Zeckensack's Glide wrapper (i.e. Voodoo 1 style); that's the same glitch under Simons' coat.
The Lucky Money club freezer on the Voodoo 3. Note the darkened bit of reflection.
D3D10 with SimulateMultipassTexturing=true. No glitches. Note that SimulateMultipassTexturing=true did cause glitches for the intro scene.
D3D10 with SimulateMultipassTexturing=false; this matches the way the Glide, D3D and OpenGL renderers look.
Zeckensack again matches SimulateMultipassTexturing=true.


Technical background

The sky is a translucent layer with both a diffuse texture and lightmap. The game uses src + (1-src)*dest blending for this. With multitexturing this ends up being (diffuse*light) + (1 - diffuse*light)*dest.

Without multitexturing, however, the same is done for the diffuse texture, but the lightmap is modulated over this, using src * dest blending. The result of this is (diffuse + (1 - diffuse)*dest)*light = diffuse*light + (1 - diffuse)*light*dest. So the difference is in (1 - diffuse*light) vs (1 - diffuse)*light.

The D3D10 renderer uses dual-source blending with src0 + src1*dest. Then, src0 is set to diffuse*light as with multitexturing, but src1 is set to (1-diffuse)*light. This results in diffuse*light + (1-diffuse)*light*dest as with multi-pass rendering.

Created: Apr 27 2014
Modified: Feb 08 2020