 Here we see a 3D triangle in the top right, its projection onto the viewplane on the left, and the texture for this triangle in the bottom right. Point A on the texture corresponds to point A on the triangles, and likewise with points B and C. To draw the texture on the triangle, we must find the points on the texture that correspond to the points of the triangle on the viewplane. Previously, we learned how to interpolate coordinates from one triangle to another such that we can draw the pixel contents of one triangle into any other. Using this technique to map coordinates directly from the viewplane triangle to the texture triangle produces an affine texture mapping, but there's a problem. Here we have two triangles representing a flat rectangular surface that leans away from our viewpoint, and as you can see, affine mapping draws textures with odd distortion. The issue is that affine mapping doesn't correctly account for 3D projection. If the relationship between the three triangles were linear, we could use a linear mapping from the viewplane to the texture to get correct results. However, the relationship between coordinates of the viewplane triangle and coordinates of the 3D triangle is not linear thanks to the effect of 3D projection. To get a perspective correct texture mapping, we must first use a projection transform to map coordinates from the viewplane triangle to the 3D triangle, and then use a straight linear transform to map coordinates of the 3D triangle to the texture triangle. To understand how to account for perspective, consider again what a rectilinear projection does. Here we have a 3D line viewed side on. When we account for perspective, we scale the points closer to the axis, and this scaling effect increases as we move away from the camera. Because the scaling increases exponentially, the perspective adjusted points no longer line up, instead forming a curve. Recall the formula for perspective. The ratio between our 3D chord and its Z distance from the camera is equal to the ratio between the perspective adjusted chord and the viewing plane's Z distance from the camera. So to get the adjusted chord, we multiply our 3D coordinate by the ratio of A1 over A2, the ratio between the Z distance to the viewing plane and the Z distance to the 3D coordinate. This ratio we can call the scaling factor, the amount by which we adjust the coordinate. So here if we have the 3D coordinates and the Z distance from the camera to the viewing plane, we can compute the scaling factors, and with them we can then compute the actual perspective adjusted coordinates on the viewing plane. The middle point, for example, has a scaling factor of 3 over 7, or about 0.429, which we multiply with 3 to get the perspective adjusted Y chord 1.287. An important thing to note here is that though the 3D coordinate in the middle lies directly 50% between the other two coordinates, we can't say the same of its corresponding perspective adjusted coordinate and the other coordinates. 1.287 is actually about 64.4% of the way from 0.6 to 1.66. Surprisingly however, if we linearly interpolate between the scaling factors of the two endpoints by 64.4%, we get 0.429, the same scaling factor for the middle 3D coordinate. In fact, this holds in the general case. While we won't prove it mathematically, using the alpha value of a point between the perspective adjusted endpoints, that is the distance by percentage between the two adjusted endpoints, using that alpha to interpolate between the endpoint scaling factors gets us the scaling factor which produces that perspective adjusted endpoint when multiplied by the corresponding 3D point. So imagine we had just the endpoint coordinates and the scaling factors. If we want to find the 3D coordinate corresponding to a point in between the viewplane coordinates, we can do so by interpolation. First we determine the alpha value, the percentage distance between endpoints, then we use that alpha value to interpolate between the scaling factors. Then we multiply our viewplane coordinate by the inverse of that scaling factor to get the corresponding 3D coordinate. Thus, we can use interpolation to go from viewplane coordinates to 3D coordinates even though the relationship is not linear. Just to be complete, let's look at an example in all three dimensions. Here we have a line running from negative 8, negative 6, 35 to 18, 21, 50, and our camera is 10 units away from the viewplane. The scaling factors for these two points are then 10 over 45, which is about 0.222, and 10 over 60, which is about 0.167. We get the x and y coordinates on the viewplane by multiplying our 3D x and y by their scaling factor, so we get negative 1.78, negative 1.33, and 3.01, 3.51. If we now want to find the 3D coordinate that corresponds to a point along the line in the viewplane, we start by finding the alpha of that point between the two adjusted endpoints, in this case 0.27. With that alpha value, we interpolate between the scaling factors to get 0.207. We then get the x and y of the corresponding 3D coordinate by multiplying the viewplane coordinates, negative 0.49, negative 0.023, by the inversion of 0.207, which gets us negative 2.37, negative 0.11. To get the z coordinate, we simply divide the scaling factor by the viewplane distance and then subtract the viewplane distance, yielding in this case approximately 38.3. Now, if we have a full 3D triangle, we can of course draw it on the viewplane by finding the scaling factors of the vertices and perspective adjusting the vertices. To find the 3D points that correspond to points along the viewplane triangle edges, we can interpolate along the edge lines like we just demonstrated, but what about points inside the viewplane triangle? Well, we can use the same basic trick we used when interpolating between the interior points of two dimensional triangles. We first find the corresponding 3D endpoints along the edges of an intersecting line and then interpolate along this line to find the corresponding interior point. Note that we don't need to compute the z values of the intersecting line endpoints, just their scaling values, which we find directly by perspective adjusted interpolation. Most commonly with this technique, we simply use the intersecting horizontal line because it's trivial to find and easy to find the alpha values of the endpoints along the triangle edges. We could just as easily use the intersecting vertical line, but horizontal is the common choice. So now we know how to map coordinates from viewplane points of a triangle to corresponding 3D points, and so we can perform a perspective correct texture mapping by then mapping those 3D points to their corresponding texture coordinates. However, because there is a linear relationship between the 3D coordinates and the texture coordinates, we can get away with doing one mapping for each point rather than two. Once we have our viewplane vertices, we can simply substitute the texture coordinate values for the x and y of the 3D triangle when we map from the viewplane to the 3D triangle. Another way to think of it is that we can map directly from the viewplane to the texture if we account for perspective with the scaling factors of the corresponding points. Now that we can map from viewplane coordinates to texture coordinates, we can get color values from the texture using the same nearest neighbor or bilinear filtering techniques that we used when resizing rectangular images. Bilinear filtering, of course, generally gives better results. Consider, however, the weakness of bilinear filtering when effectively shrinking the image. If you want to resize this image into a 2x2 pixel image with bilinear filtering, we find the 4 points on the image corresponded to the 4 pixel centers and then average the 4 pixels around each point, factoring the relative closeness to the point. The problem here is that, out of all the pixels in this image, our shrunk conversion only accounts for 16 of the original pixels. A better, but more expensive approach would be to identify the areas of the original image that correspond to our 4 destination pixels and then average together the pixels in each area to generate our 4 pixel values. So the top left destination pixel, for example, would be an average of every pixel in the top left quadrant of this source. By averaging these areas, we get a shrunken image that more accurately reflects the original, but at the cost of more processing work. For another extreme example, consider this 100x100 pixel image. If we shrunk it to just one pixel using bilinear filtering, we would get a single green pixel of the same shade as the center green square. A more accurate rendering, however, would average together every pixel in the image, resulting in a shade of red, similar to that in the original image, but with a small mix of the green. A red pixel with a hint of green, of course, looks very different than a green pixel, so we'd get a very different result. So the takeaway is that straight bilinear filtering is not ideal for 3D texture mapping when a surface is far away from the camera, because this typically means that the texture is getting significantly shrunk, or minified, as we say. A way to address this problem is to use what are called MIP maps. MIP is short for the Latin phrase multum in parvo, meaning much in little, as in much detail in little textures. The idea of a MIP map is that we pre-compute minified versions of our original texture using the area sampling technique instead of bilinear filtering. A rendering engine then chooses the best matching version of the texture to render from based upon the distance of the surface, the further away, the smaller the chosen image. Here, for example, you see the original full-sized texture on the left, and then successively shrunken half versions on the right. Because these minified versions are generated by area sampling, they more accurately reflect the original image than if they were generated by bilinear filtering. Even though we then use bilinear filtering to ultimately draw from these textures, the results are better for distant surfaces than if we instead always drew from the original texture. Depending upon the precise hardware and software, these MIP maps are either generated before a program is run, or actually just when the textures are loaded. A MIP map being larger than the original image, of course occupies more texture memory, but for most modern hardware, MIP maps usually improve rendering performance because the smaller texture versions have greater memory locality and are thus faster to sample. When moving closer towards and farther away from textures, the transition from one texture of a MIP map to the next might show up as a sudden change in texture quality. To smooth these transitions, we can use trilinear filtering, which interpolates between the results of bilinear filtering on the two closest matching MIP map textures. Anisotropic filtering further improves upon MIP mapping to better render textures on surfaces angled relative to the camera. Here on the left, the portion of the runway further away from the camera appears muddy with trilinear filtering, but on the right, the same portion of the runway appears much clearer with anisotropic filtering. What's happening here is that the portion of the texture represented by each square area in the rendered surface should actually be trapezoidal in shape, not square. Here for example, this portion of the runway in our rendered image corresponds to this trapezoidal area of the texture. As we move away from the camera, that portion of the runway effectively requires more texture minification, and the more minification, the more the mismatch between squares and trapezoids produces muddy texturing. For maximum quality, we would find the precise trapezoidal area in the texture that corresponds to each pixel and average those textiles proportional to the area they occupy in the trapezoid. However, we can get more efficient and nearly as good results using anisotropic filtering, which like mint mapping involves pre-computing minified versions of textures. The difference is that in anisotropic filtering, we pre-compute minified versions of non-proportional scale, like say a version of the texture that's the same width as the original, but half as tall. Such a version of the texture would be appropriate to sample when rendering, say, the distant pixels of a corridor wall as you look down the corridor, because those pixels need more vertical minification than horizontal. To get even more accurate results, especially for surfaces at diagonal angles, we can approximate sampling of a small trapezoidal area of the texture by sampling from more than four pixels around our sample point and or weighting the interpolation of these adjacent pixels to approximate the effect of sampling from a trapezoidal area. Another approach is to pre-compute trapezoidal versions of the texture. These details are beyond our scope, so we won't cover them here. Suffice it to say that anisotropic filtering has many variants with different trade-offs between performance and quality.