CS559 Spring 2021 Sample Solution - Workbook 9
Written by CS559 course staff
Using textures is notoriously tricky because there are a lot of steps involved. Fortunately, THREE makes it remarkably easy - it takes care of all of the little details, allowing you to focus on the big parts (setting coordinates, defining the texture image).
If you want a sense of how much had to happen to get a cube with texture on it, have a look at the 2015 example! And this doesn’t do any lighting!
Fortunately, THREE takes care of all of this for us. And it gives us access to lots of fancier kinds of texture things as well.
The “ThreeJS Fundamentals” website has a nice tutorial on texture mapping that gets to some of the more advanced issues that we will get to later.
The Complete Example
We’re going to do this backwards. Here is a complete example program. It’s in 09-04-01.js. It’s the two triangles from Page 1 (Meshes: What you need to know), with an extra plane and a texture added. The plane is so you can see what the texture looks like.
We’ll explain how it works in the following, but you might want to have a look first.
Box 2: Texture Coordinates in THREE
Most THREE primitives have reasonable texture coordinates defined. For example, the demo in Box 1 has its u,v going from 0 to 1 over the course of a plane.
For user-defined geometries (of class
Geometry), we have to provide texture coordinates ourselves. Just like with colors and normals, a vertex may be “split” so that it has a different texture coordinate for each face it is part of. However, unlike colors and normals, the texture coordinates are not part of the
Face3 data structures.
Texture coordinates must be provided as an array of arrays of arrays of vectors. The
faceVertexUvs property of a
Geometry has type
Vector2. It is triple nested because:
- Each vertex needs a 2D coordinate (a
- Each face has 3 vertices
- Each layer has an array of faces
- Each geometry can have multiple layers of texture coordinates.
We won’t use layers now. So we’ll always make the top level be an array of length 1.
Here’s an example from the code:
The first line of this snippet creates the
faceVertexUvs array with a single layer. For now, that layer is empty (it has no faces in it). The last line adds the texture coordinate for the particular triangle. Notice that it adds to the layer (
faceVertexUvs). What it adds is the information for the face: the coordinates for the three vertices of the triangle. Each coordinate is a
Vector2, and the face is an array of three of them.
To see the nesting, let me write the code in multiple lines:
Texture Image Loading in THREE
In order to have a texture image, we need to get the image from somewhere. Usually, this is from some image file.
Loading an image into a texture is tricky. We need to load the image. We need to wait until the image is loaded. We need to process the image and put it into a special format. We need to attach the image to its surface.
The line of code is:
Texture Usage in THREE
Once a texture is created, we simply need to tell the material to use it in order to get the colors on the triangles. This is done by specifying a
map parameter to a texture. So, in the example:
We can specify other properties for the material. In the example, I specified
roughness. You can even specify
color, and this color will be combined with the colors from the texture. And of course, with a
MeshStandardMaterial, the colors are affected by lighting as well. If you want to use textures but do not want the color affected by lighting, use a
THREE allows us to control many properties of how textures are used. These will make more sense once we learn about how texturing works in class. For now, the default parameters will probably be fine.
It’s so easy to load and use textures with THREE that you might forget how many you’ve loaded. In general, using lots of textures can be a problem: they take up a lot of memory.
For example, in the example code (
for_students/09-04-01.js), the texture file
/images/UV_Grid_Sm.jpg is loaded three times. A separate copy will be loaded for each instance of
TwoTrianglesTextured2 that we create. Right now, there is just one of each (so that’s two copies of the texture - a third is used for the plane). If we made a lot of these objects, the memory usage would add up fast.
Therefore, when we use textures, we need to be careful to re-use things that we’ve loaded already and not load multiple copies. In fact, we might want to try to re-use the same texture over and over to save memory. Think about this when you make your own objects for future projects.
Summary: Textures in THREE
That was a quick summary of how textures are applied in THREE. On Page 5 (Dice and Dominos (Exercise 2 and 3)), you can try doing it yourself!