Perspective-Correct Texture Mapping Basics
The goal of this section is to create a texture-mapper that is perspective-correct so that as the polygon gets smaller with distance, so does the texture. Notice that a texture can be smaller than the actual polygon you're mapping to. In this case, the texture is tiled onto the polygon, as shown in Screenshot.
Screenshot When mapping a texture that is smaller than a polygon, the texture is tiled across it.
A pixel in a texture is commonly called a texel, short for "texture element." Keep in mind that your textures will be scaled based on how close the camera is to the polygon, so the texels will scale as well. In other words, a texel is rarely the same size as a pixel in the view window. You can do texture mapping in two ways: Map every texel to a pixel, or map every pixel to a texel. Mapping each texel to a pixel is more work than it's worth because sometimes not every texel in a texture is drawn (such as when you view a texture from an angle or from far away). Also, texels themselves can be rotated and scaled based on the texture orientation. Basically, if you map texels to pixels, you're drawing a solid polygon for each texel and doing some overdrawing (drawing some pixels more than once) along the way. Instead, you'll map pixels to texels, with the goal of drawing every pixel in the view window only once. In other words, for every pixel in the view window, you'll find the appropriate texel to draw. With this goal in mind, a good stage to do the texture mapping within the rendering pipeline is after the polygon has been scan-converted, just like you did with the solid-fill polygon renderer in the previous chapter.
Deriving the Texture Mapping Equations
I could just give you the equations commonly used for texture mapping (which are easy to find on the Internet). However, I'm going to start by giving you more background on how these equations are derived—you know, in case you're curious. Let's get started with some vector math. If you're unfamiliar with the basics of vector math, be sure to check out the introduction in the previous chapter. First, let's define how a texture is oriented on the polygon. You want a texture to be able to be offset or rotated anywhere in the polygon, so you do this with the following vectors (shown in Screenshot):
Screenshot A texture's orientation within a polygon is defined by an origin and two direction vectors.
In this configuration, the texture could be oriented in any direction. Also, the texture origin doesn't necessarily have to be within the polygon bounds. Let's make a couple rules, though. Keep the U and V vectors orthogonal to each other (in other words, at right angles to each other) because you really don't need to skew any textures onto a surface. Also, you should make sure the U and V vectors are on the same plane as the polygon. Otherwise, if the U or V vectors were pointing outward from the polygon, you could get some strange results, such as polygons that have textures that appear at an angle when you're looking at them head on. Now look at the O, U, and V vectors more closely and how they relate to what we want to do. Remember, you want to map every pixel to a texel. Before you know what texel to draw, you need to know the exact point on the polygon, P, for a point on the view window, W. Let's skip a step and assume you already have a point on the polygon, P, and you want to determine the (x,y) texture coordinate (see Screenshot).
Screenshot How a point on the polygon, P, relates to the texture's orientation.
From Screenshot, you can get the equations for x and y, which are the location within the texture:
x = |P–O|cos q2 y = |P–O|cos q1
In these equations, |P–O| is the distance from O to P, q1 is the angle between (P–O) and V, and q2 is the angle between (P–O) and U. Going a step further, if U and V are unit vectors, you can use the dot product to simplify things:
x = U•(P–O) y = V•(P–O)
Remember, it's okay if the (x,y) texture coordinates are negative or otherwise are not within the texture bounds; you're going to tile the texture across the polygon. Now let's go back a step and find the point P on the polygon from the point on the view window, W (see Screenshot). You already know which polygon W points to because, at the texture-mapping stage, the polygon is already projected onto the view window.
Screenshot Projecting a point on the polygon, P, onto a point on the view window, W.
Wx = d Px/Pz Wy = d Py/Pz Wz = d Pz/Pz
Wz is equal to d, the distance from the camera to the view window. Converting this to vector form, you get this:
W = (d/Pz)P
You need to find P, so solve this formula for P:
P = (Pz/d)W
So, to find P, you need to find Pz, which is the distance from the camera to the polygon along the z-axis. To find Pz, you need information on the plane formed by the polygon. Use the equation of the plane, which basically just says that the normal of the plane, N, and any line in the plane are orthogonal to each other. The line to use is (P–O) from Screenshot because this vector is in the same plane as the polygon. Let's begin:
N•(P–O) = 0 N•O = N•P
Here insert the previous equation for P and solve for Pz:
N•O = N•((Pz/d)W)) N•O = (Pz/d)(N•W) Pz = d (N•O)/(N•W)
Now plug Pz back into the previous formula and get P:
P = W(N•O)/(N•W)
x = U•(W(N•O)/(N•W)–O)
y = V•(W(N•O)/(N•W)–O)
Now you have some texture mapping equations. However, these are pretty nasty-looking equations. Luckily, they reduce a bit when you apply some of the rules of vector math. Let's start with the x coordinate:
x = U•(W(N•O)–O(N•W))/(N•W)
First, apply the vector triple product definition AxBxC = B(A•C)–C(A•B)
x = U•(NxWxO)/(N•W)
Next, apply the scalar triple product definition A•BxC = AxB•C
x = (UxNxW•O)/(N•W)
If you assume that UxN = –V and N = UxV (that the polygon normal and the two texture directions are all orthogonal to each other), then:
x = (–VxW•O)/(UxV•W)
Finally, you get this:
x = (VxO•W)/(UxV•W) y = (OxU•W)/(UxV•W)
This is much easier, but you can continue simplifying. Notice that when you're texture-mapping a specific polygon, the only thing that's going to change as you render a polygon is W. The vectors O, U, and V don't change, so you can precompute those cross-products beforehand for each polygon:
A = VxO B = OxU C = UxV
So when you're texture mapping, you just calculate this:
x = A•W/C•W y = B•W/C•W
Whew! That's it—you've found some texture mapping equations. Now you'll put these equations to use and create a texture-mapper.