# Texture Mapping, Part 3

The theme for this part of the reading is thinking about the underlying
reality of a texture, which is an array of texels. You can think of the
texture as a function from a pair of real numbers, `(s,t)`

, to either

- bytes for
`THREE.LuminanceFormat`

, or - triples of bytes for
`THREE.RGBFormat`

A *byte* is, for our purposes, an integer from 0 to 255,
inclusive.

In other words, a texture is a function with a discrete value, not a continuous value.

## Linear vs. Nearest

Remember this demo of mapping a flag onto a plane:

In the source code, there are two settings that were not explained:

var obj = new THREE.DataTexture( array, width, height, format); obj.minFilter = THREE.NearestFilter; obj.magFilter = THREE.NearestFilter;

Now is the time to explain these filters.

Going back to our observation that textures are discrete-valued
functions, let's consider the NASCAR

flag (black and white
checks). Actually, let's simplify it much further. Let's make the flag
just 4x4 checks and consider just one dimension. Thus, we get a
function like this:

s | value | color |
---|---|---|

0-0.25 | 0 | black |

0.25-0.50 | 255 | white |

0.50-0.75 | 0 | black |

0.75-1 | 255 | white |

It might look like this:

What's the value when the parameter is 0.49? What's the value when the
parameter is 0.51? It's an abrupt change, isn't it? When we do this, we
are choosing the *nearest* texel, and we expect there to be
abrupt transitions between texels.

What if we want a smoother transition between texels? In this case,
we could *interpolate*. That is, we consider the center

of
each texel, and when a parameter falls between the centers of two
texels, we can linearly interpolate between the two possible output
values.

Here's a table that shows the centers.

s | center | value | color |
---|---|---|---|

0-0.25 | 0.125 | 0 | black |

0.25-0.50 | 0.375 | 255 | white |

0.50-0.75 | 0.625 | 0 | black |

0.75-1 | 0.875 | 255 | white |

It might look like this:

The values that are in between zero and 255 will be shades of gray in a luminance format. In an RGB format, we would separately interpolate between values for red, green and blue. Note that we're not going to worry about what happens at the edges. We just want to understand the basic idea of interpolating between texels.

When viewed like this, with very few texels, the linear version usually
looks ... er, ... bad. Fuzzy at best. However, in real life, when we
load an image to use as a texture, the texture often has lots of texels,
and interpolation looks pretty good. In fact, it is
the *default* for Three.js. (All those images of Buffy used
interpolation, and they looked fine.) Nevertheless, you should know
about `THREE.NearestFilter`

, in case you want it,
particularly if you're using a small image with sharp edges.

## MagFilter versus MinFilter

The discussion above is entirely based on when the texture is smaller than the fragment. We've got, say, 100 pixels to color, and only 4 texels, so the texture coordinates of those pixels will be 0.01, 0.02, 0.03, ... Hence the choice about interpolation and nearest.

This situation is when we use the `minFilter`

.

There's an analogous situation that occurs, perhaps when an object's
projection is small and the texture is large, that there are more texels
than pixels. Then, the texture parameters might actually skip over some
texels. We can still make a choice about whether to choose the nearest
texel or interpolate between two. That situation is when we use
the `magFilter`

(for *magnification*).

In practice, we often choose the same value for both, because we either want interpolation or we don't.

## Working With Arrays

Should you want to compute your own texture, as we did in creating these flags, you need to know a little about the underlying representation.

You know, of course, that an image is a rectangular array of pixels,
and each pixel is either one byte (`THREE.LuminanceFormat`

)
or three bytes (`THREE.RGBFormat`

). However, arrays of two
or three dimensions are a higher level of abstraction. The underlying
reality, in memory and in the graphics card, is that all arrays are
one-dimensional.

There are several choices for building multi-dimensional arrays out of
1D arrays. We will only discuss *row-major order*. Consider a
luminance array of 12 bytes. The underlying reality is that the bytes
are numbered like this:

00 01 02 03 04 05 06 07 08 09 10 11

In almost every high-level language, array elements are numbered
starting at zero, because at a lower level, the *index* indicates
how much to add to the address of the beginning of the array in order to
get to the desired element. In other words, the index is
a *distance*.

Now, we want to build a 2D array out of these 12 elements. Let's suppose it's 3 rows and 4 columns, with the rows numbered 0-2 and the columns 0-3. The array looks like this:

col0 | col1 | col2 | col3 | |
---|---|---|---|---|

row0 | 0,0 | 0,1 | 0,2 | 0,3 |

row1 | 1,0 | 1,1 | 1,2 | 1,3 |

row2 | 2,0 | 2,1 | 2,2 | 2,3 |

What we want, then, is a way to convert a pair of values (row, col) into a distance from the beginning of the array.

We now have a choice:

- do we put the first row consecutively in memory, then the second row, and so forth? This is called row-major order.
- do we put the first column consecutively in memory, then the second column, and so forth? This is called column-major order.

## Row Major Order

We choose row major order. To determine how to implement it, let's first write down the distances:

col0 | col1 | col2 | col3 | |
---|---|---|---|---|

row0 | 0 | 1 | 2 | 3 |

row1 | 4 | 5 | 6 | 7 |

row2 | 8 | 9 | 10 | 11 |

You'll notice that each cell's distance is one more than the cell to
its left, and **4** more than the one above it. The 4
comes from the fact that the length of each row (equivalently, the
number of columns or the *width*) is 4.

A little playing with numbers, and you'll determine the following formula:

index = 4 * row + col

This is a pretty simple formula. Note that if we numbered rows and columns starting at 1, the formula would become:

index = 4 * (row-1) + (col-1)

which is much uglier. The general formula is, of course:

index = width * row + col

## 3D Arrays

I'll leave as an exercise for the reader building even higher
dimensional arrays out of 1D arrays using row-major order. However, you
can think of the `THREE.RGBFormat`

as a 3D array, where we
have a 2D array of RGB triples, and the RGB triples are
the *innermost* dimension. Thus, the layout is:

col0 | col1 | col2 | col3 | |
---|---|---|---|---|

row0 | 0,1,2 | 3,4,5 | 6,7,8 | 9,10,11 |

row1 | 12,13,14 | 15,16,17 | 18,19,20 | 21,22,23 |

row2 | 24,25,26 | 27,28,29 | 30,31,32 | 33,34,35 |

You can see there are 36 bytes (numbered from 0 to 35), and the
distance (also called the *stride*) from row to row is now 12 (4
columns of 3 bytes each). The stride from column to column is just 3
(because you're skipping one RGB triple).

The total size, in bytes, of the array is

width * height * 3

The formula for the index of a byte is:

index = (width*3) * row + 3*col

You can probably easily derive the more general formula.

## NASCAR Flag Code

Now we're prepared to understand the code to build a flag, or any texture, in raw JavaScript. Let's start with the easiest one, the NASCAR flag (black and white checks):

This code allocates a special kind of JavaScript array, in which the
datatype of each element is a byte, also known as an
*unsigned 8-bit integer* value, or `Uint8`

.

Also, because most textures work best if their dimensions are powers of two, the size is specified indirectly as the log of the size, so evaluating this with an argument of 5 gives you a 32 x 32 flag, and 6 gets you 64 x 64, etc.

In the code above, we keep a separate counter, `n`

, which is
just the distance from the beginning of the array. We can then use
a clever

trick to determine if the row and column are
different *parity* (even/oddness). The JS code `x&1`

extracts the right-most bit of the number x by ANDing it with a 1. Thus
the following expression alternates between true and false as we
traverse the array:

if( (i&1) != (j&1) ) { ...

Can you think of a way to simplify this code?

## Checkerboard Flag

The checkerboard flag is similar, but of course, we have to set three values to get red and black squares:

*The remainder of this reading elaborates on the implementation of the creation
of the US Flag textures — you can just skim this part.*

## US Flag

Those were both pretty easy. Now, let's look at the US Flag, where we really have to think about row-major order. We'll start with black and white, where we have black stripes where the real flag has red stripes, and black for the union (the blue part). It's not important to understand every line of this code, but you should understand how we figure out the color (black or white) of various kinds of texels:

The `doStripe()`

function is essential, and it relies on a
function to set a byte of an array using row major order. I called this
function `rowMajorAset()`

, from a CommonLisp function of a similar
name:

## RGB Flag

If you're not too exhausted, let's look at computing the red, white, and
blue flag. Again, all the details aren't important, but I do want you
to understand how to set elements of the array, which is done with this
function, `TW.rowMajorAsetRGB()`

:

Here's the finished code
for `TW.createUSFlagRedWhiteBlue()`

. Skim most of it and just
look at the lines that fill in the stripes and the union (around line 30).

That's it. Layers of abstraction help a lot.

## Compression

Note that this discussion has all been about *uncompressed*
images. Most of the image file formats that you are familiar with
(GIF, JPEG, PNG) are *compressed*. Part of the Three.js code
that loads textures has to *uncompress* the file so that the
texture is ready for use.

## Summary

Here's what we learned

- When accessing the texture array, using texture parameters, we can
either take the value of the
*nearest*texel, or we can*linearly*interpolate. - The default setting in Three.js is
`THREE.LinearFilter`

. - We can build 2D and 3D arrays out of 1D arrays by using
*row major order*.