 
                                     Member of the Internet Link Exchange

                      Adventures In Texture Space 

                                     Bob Pendleton 

                                      bobp@pendleton.com 

A while ago I decided to figure out how to do game style texture mapping so I could write about it. People were making
such a big deal out of it I figured it was a good subject for an article. And I figured it couldn't be as hard as people made it
sound. 

As I usually do when I start researching a graphics topic I first looked up texture mapping in the standard graphics text
books. That was fun. I didn't find one text book with more than a page on the subject and they all seemed to use the same
illustration. I looked in "Graphics Gems" 1, 2, and 3 and didn't find much there either. Then I got a clue thumbing through
Foley & van Dam. I happened to look at a section on ray tracing and found some useful information. I looked back in
"Gems" under ray tracing and found more useful information. All in a section on ray tracing. 

After looking in my favorite books I went out looking on the net and found another reference that I'm going to add to my list
of standard references. It's the "PC Game Programmers Encyclopedia" or PCGPE for short. PCGPE isn't a book. It's a
program and a collection of articles put together by folks on the net. I found a copy on teeri.oulu.fi in
/pub/msdos/programming/gpe. The quality of the articles I've looked at so far ranges from poor to excellent. But, it seems to
be a good place to pick up basic ideas and to learn the buzz words you need to find details other places. 

The PCGPE has two articles on texture mapping. One of the articles, in file texture.txt, by Sean Barrett is a very good
reference on how to optimize the inner loop of a game quality texture engine. But, it doesn't give much information about
how to draw a textured polygon. It really glosses over the math, describing a key transform as "magic." If you want to write
a fast texture engine you want to read it. But, I spent a week digging around in math books I haven't opened in 15 years to
figure out the "magic." 

So, what is all the mystery? It has to do with spaces and transformations. When you build a model of a world you do it in
what is called, for obvious reasons, world space. You use normal x, y, z coordinates and you use units like meters, feet,
millimeters, or inches or what ever makes sense to you. 

When you want to draw a view of your world into a frame buffer you use your favorite 3D graphics package to create a
transform that when applied to points in world space converts them to points in view space. View space also has x, y, and z
coordinates but in view space the coordinates have the special property that when you divide view space x by view space z
you get a frame buffer x coordinate and when you divide view space y by view space z you get a frame buffer y coordinate. 

Let me say that again, to draw your 3D world into a frame buffer you start with world coordinates, call them wx, wy, wz,
transform them to view space coordinates, vx, vy, vz, and then by division you transform them into 2D frame buffer
coordinates, fx, and fy. If those coordinates are the vertices of a polygon you can then use the information in my last column
(#5) to draw the polygon in the frame buffer. So far so good. 

We want to paint the polygon with a picture, called a texture, instead of a solid color. And, we want the texture to scale and
rotate with the polygon. We have a transform that converts points in view space to points in the plane of the frame buffer.
What we want is a transform that maps frame buffer coordinates back into view space coordinates and also maps view
space points onto the texture. 

Given that kind of a reverse transform we could use it with an ordinary polygon drawing routine to draw polygons filled with
the contents of the texture rather than a solid color. We'd just use the new transform to convert each the x, y of each pixel in
the polygon into the x, y of a pixel in the texture. 

To keep every thing straight I'm going to use "u" and "v" for texture "x" and texture "y". It's traditional usage and it saves
typing. 

Turns out that there is a little bit of mathemagic that gives us that transform with very little effort. It lets us transform screen
coordinates into another space, called texture space, such that x / z, and y / z in texture space gives us two numbers, u and
v, that are coordinates in the texture. 

First off you need 3 points in view space, that's very important, they must be in view space, and they must not be in a line.
Any 3 points that are not in a line define a plane. We are going to construct this plane so that it is parallel to the plane of a
polygon or polygons you want to paint with a texture. 

Given 3 points, p0, p1, and p2, you compute the differences between the points giving 3 vectors P, M, and N, like so: 

 
    px = p0.x;
    py = p0.y;
    pz = p0.z;

    mx = p1.x - px;
    my = p1.y - py;
    mz = p1.z - pz;

    nx = px - p2.x;
    ny = py - p2.y;
    nz = pz - p2.z;

The vector P is the where the texture origin in view space. M and N are the textures U and V direction vectors in view
space. If the lenght of M and N aren't the same then the texture will be mapped onto a rectangle instead of a square. 

In the demo code I actually use the first 2 and the last point in a polygon outline for the 3 view space points. That's sort of a
cheat. You can use any three points as long as they are in the same plane as the polygon you want to draw. In fact, if you
have a number of polygons that are all in the same plane you can compute this transform once and use it for all the polygons.

The order of the subtracts above is also important. I compute the elements of M as p1 - P but compute N as P - p2. This is
because in most frame buffers Y increases going down the screen and X increases going across the screen. If Y increased
going up the screen you would compute M = P - p1. If you get the direction of the subtraction wrong when you rotate the
polygon one direction the texture rotates another direction. The effect is very weird looking and kept me up most of a night
before I figured it out. 

Once you have the first three vectors you take three cross products, like so: 

 
    hx = (py * mz) - (pz * my);
    vx = (pz * mx) - (px * mz);
    ox = (px * my) - (py * mx);

    hy = (py * nz) - (pz * ny);
    vy = (pz * nx) - (px * nz);
    oy = (px * ny) - (py * nx);

    hz = (ny * mz) - (nz * my);
    vz = (nz * mx) - (nx * mz);
    oz = (nx * my) - (ny * mx);

Giving 3 more vectors. These vectors are the transform we are looking for. The cross products gives you a vector at 90
degrees to the 2 vectors you are crossing. That is important. We use these vectors in the following way to transform frame
buffer coordinates (fx, fy) into texture coordinates (u, v). 

 
    x = ox + (hx * fx) + (vx * fy);
    y = oy + (hy * fx) + (vy * fy);
    z = oz + (hz * fx) + (vz * fy);

    u = (x / z);
    v = (y / z);

This code looks very much like the transformation code in a 3d graphics package, take a look at 3d.h and 3d.c for example,
it looks the same because it does the same job. 

The last bit of mathemagic is that the points p0 and p1 in view space maps onto 0 and 1 on the X axis in texture space and
p0 and p2 maps onto 0 and 1 on the Y axis in texture space. This means that if the texture is, say, 128x128 pixels then the
texture subscript, tu is computed from u by 

tu = (u * 128) ^ 128; 

If u is in fixed point then 

tu = (u << 7) & 0x7f; 

tv is computed the same way. The& is important to keep the texture coordinates inside the texture. It also means that if the
polygon is larger than the texture the texture will be tiled across the entire polygon. 

What follows is the actual span filling code from an early version of the texture code in the demo that goes with this article.
The first part computes the texture space x, y, and z for the first pixel in the span we want to fill. 

 
    x = ox + 
        (hx * (ix1 - fp2float(sXCenter))) + 
        (vx * (iy - fp2float(sYCenter)));

    y = oy + 
        (hy * (ix1 - fp2float(sXCenter))) + 
        (vy * (iy - fp2float(sYCenter)));

    z = oz + 
        (hz * (ix1 - fp2float(sXCenter))) + 
        (vz * (iy - fp2float(sYCenter)));

     The loop continues for each pixel in the span. 

 
    do
    {

     Here we compute the indices into the texture we are drawing on the polygon. 

 
        iu = ((long)(x / z * 128)) & 0x7f;
        iv = ((long)(y / z * 128)) & 0x7f;

        framebuffer[iy][ix1] = texture[iv][iu];

     This part steps to the next point in texture space. 

 
        x += hx;
        y += hy;
        z += hz;

     And this part steps to the next pixel in the frame buffer. 

 
      ix1++;

    } while (ix1 < ix2);

Now comes the hard part, making it run fast. 

The example code assumes floating point arithmetic. Even the demo program is written using lots and lots of floating point
arithmetic. When I'm coding a graphics algorithm for the first time I use floating point, even double precision, arithmetic
because I don't have to worry about arithmetic precision problems caused by fixed point arithmetic. The general rule is
reduce the sources of error as much as possible. After the code is working you can study the number ranges actually used in
the code and convert it to fixed point if needed. 

The most obvious way to speed up this code is to use fixed point arithmetic. That works. But, it won't get you to speeds
anything like what you need for a good game and virtual reality is even more demanding. 

In games the usual solution to the speed problem is to restrict the way you can change your point of view. By making it so
that you can only look left and right, not up and down, they force texture space z to be constant for horizontal spans in floors
and ceilings and constant for vertical spans in walls. Texture space z is constant for horizontal spans when hz is 0 and for
vertical spans when vz is zero. Texture z is constant when all the pixels are the same z distance from the viewer. 

When texture space z is constant you can move the divides out of the loop. On a PC each of the divides costs more than 40
cycles which means that if the divides are left in the loop it costs around 100 cycles to draw 1 pixel. If you move the divides
out of the loop then, according to Sean Barrett, you can draw a textured pixel in about 5 cycles. If you are drawing
320x240 images on a DX2/66 this is the difference between being able to generate around 8 frames per second and being
able to generate 160 frames per second. (Not that your CRT can display 160 frames per second, at least mine can't.) 

This approach works fine for your typical dungeon crawling game but it isn't any good for flight simulators or for use with
head mounted displays or even for the simple task of texturing the surface of objects in your world. In all these cases you
have to be able to draw textures on arbitrary planes, not just flat walls, ceilings, and floors. 

I tried out a lot of different ways of doing fast arbitrary plane texture mapping. One approach I tried was to break polygons
into little pieces and do 2D texturing on the pieces. This sort of worked. The texture didn't distort to badly, but you could
see the seams between the pieces. I'm pretty sure you could get rid of the seams but the cost would be too high to make it
worth while. 

The technique that worked best for me is straight out of the computer graphics literature, good old piecewise linear
interpolation. The idea is that instead of computing the exact u, v for each pixel on a span you compute the exact value for
every Nth pixel and use linear interpolation to estimate the u, v for the pixels in between. 

Linear interpolation worked so well that I decided to check out some higher order forms of interpolation. First I did a test to
find out what kind of a function the texture space transformation really is. I didn't trust my self to figure it out from the math...
So I built a difference table while drawing a textured polygon. 

Difference tables are a fun bit of mathematics. It works like this; 

Compute a table of exact values of a function. Then compute the differences between those points. If the differences are all
the same then the function is a linear function. If the differences are not all the same then look at the differences between the
differences (called the second differences.) If the second differences are all the same then the function is a quadratic. If the
second differences aren't all the same then if the third differences are all the same you have a cubic function. Just keep going
until you find Nth differences that are all the same or run out of precision in your floating point numbers... 

The difference table for a function shows the original values, and the differences out to where the constants stop changing. 

For example, the difference table for a simple linear function might look like: 

      
     f(x)    1st diff

      1    |
           |   1
      2    |
           |   1
      3    |
           |   1
      4    |

and for a simple quadratic function like this: 

      
     f(x)  | 1st diff | 2nd diff

      1    |
           |    1
      2    |          |    1
           |    2     |
      4    |          |    1
           |    3     |
      7    |          |    1
           |    4
     11    |

I modified my textured polygon routines to do a running 4 level difference table for all the u coordinates for every span of
every polygon it drew. I generated about half a megabyte of data every time I ran that test. Here is a chunk of one of the
difference tables: 

      
           f(x)         1st Diff       2nd Diff       3rd Diff       4th Diff

      0.999259732142 0.015727759567 0.000071746051 0.000000489814 0.000000004449
      0.983531972575 0.015656013516 0.000071256238 0.000000485365 0.000000004398
      0.967875959059 0.015584757278 0.000070770872 0.000000480967 0.000000004348
      0.952291201781 0.015513986406 0.000070289905 0.000000476619 0.000000004299
      0.936777215376 0.015443696500 0.000069813287 0.000000472319 0.000000004251
      0.921333518875 0.015373883214 0.000069340967 0.000000468068 0.000000004203
      0.905959635662 0.015304542246 0.000068872899 0.000000463865 0.000000004156

The first difference is constant out to 3 decimal places. That means that linear interpolation is a good approximation of the
texture transform over short runs. Out in the forth decimal place the first difference stops looking constant. The second
difference is tiny and doesn't change all that fast (look at the third difference) so quadratic interpolation looks pretty good
over longer runs. 

Even the 4th difference isn't constant... but it's starting to look more like round off error than an important value. Looks like
cubic interpolation would be as close to perfect as you can get. 

Like I said earlier, I built a textured polygon drawer that uses piecewise linear interpolation and it works very well. I also
built a polygon drawer that uses quadratic interpolation. Not piecewise quadratic, it fits a quadratic to the end points and the
center point of a span no matter how long the span is. Even miss-used like that quadratic interpolation looks very good. 

So far I've been able to get better speed out of piecewise linear interpolation but I think piecewise quadratic might be the
winner in the long run. I suspect that the number of registers that the CPU has determines which technique will be faster.
Piecewise linear interpolation doesn't need as many registers as does quadratic interpolation. That means that the newer so
called RISC architecture machines could do very well with quadratic interpolation while the widely used x86 architecture
with its small number of registers would do better with linear interpolation. 



This months demo code draws textured squares in 3 space and lets you rotate and scale them. It includes a simple fixed
point 3D graphics package, and a routine that reads 8 bit .pcx files. The demo uses textures that are 128x128 and will either
use a very obnoxious default tile or try to read the texture from a file specified on the command line. Two sample textures
are included. 

Run the program as 

     test 

     test brick.pcx 

     test tile.pcx 

The tile will show up in the middle of a 320x240 mode X screen. The type of texturing that is being done is drawn in the
upper left hand corner of the screen. The demo responds to the following command keys while it's running. 

      
     ESC -- stop the program.

     1   -- select "perfect" 2 divides per pixel texture mapping
     2   -- select linear interpolation texture mapping
     3   -- select quadratic interpolation texture mapping

     x   -- subtract 1 from the X axis rotation speed
     X   -- add 1 to the X axis rotation speed

     y   -- subtract 1 from the Y axis rotation speed
     Y   -- add 1 to the Y axis rotation speed

     z   -- subtract 1 from the Z axis rotation speed
     Z   -- add 1 to the Z axis rotation speed

     +   -- make the tile bigger
     -   -- make the tile smaller

     s   -- stop spinning
     S   -- Start over

The effect of typing the x, y, z, X, Y, and Z keys are cumulative and make the square rotate faster or slower. It is very
interesting to let it spin until it is really twisted, press s, and then switch between the different kinds of texture mapping. You
can really see the difference. 

The most interesting routines in the code are tpoly which computes the texture space transform and drives the polygon
rendering, and the 3 span drawing routines. Tspan() does 2 divides per pixel "perfect" texture mapping. Lspan() does
piecewise linear interpolation texture mapping. And, qspan() does quadratic texture mapping. 



Copyright 1993, 1996 Robert C. Pendleton. All rights reserved. 


             
