This reading covers several important issues:
As we know, texture parameters are defined in the geometry , not the material. Suppose we have a simple image texture-mapped onto a plane, as we’ve seen with PlaneBuffyTW.html, which just has two triangular faces. Given this, we can ask how the texture parameters are defined for each face. Here’s a figure that shows this:
The faceVertexUvs
property is an array of an array of
“face descriptors”. Each face descriptor is an array of exactly three “face
texture descriptors”, one for each vertex (a, b, and c). Each face texture
descriptor is a THREE.Vector2
.
With this knowledge, we can reach into a geometry object to change the default texture coordinates. We’ll return to this later in the reading.
Now let’s look at how textures work on a BoxGeometry
.
Take a look at this demo and read the code; it’s only a couple dozen lines of code, most of which you already know.
In fact, the code conceals too much. It will take some work to figure out how the texture maps onto each side of the box. Furthermore, Three.js gives us no parameters to control how the texture is mapped onto the sides. We’ll learn how to do that.
By looking at how the US Flag maps onto the box, we can deduce the texture
parameters for each side. (What are they?) We can confirm this by looking at
the source code for THREE.BoxGeometry
.
But, how to modify them? There are at least two basic approaches:
faceVertexUvs
and modify the THREE.Vector2
that have the texture parameters. This isn’t too hard for fairly simple geometries like the Plane or even a simple Box, but the indexing becomes much more difficult once you have a box with more than one segment along the width, height, or depth.To illustrate the latter approach, let’s take a look at a variation of the Box Flag that prints out the geometry of the box:
BoxFlag-v2.html: Box Flag with French Flag and Geometry display
Suppose we want to modify the front side? Which faces are those? Is there any
other way to characterize them? How about the faces with a normal vector like
(0,0,1)? Alternatively, we could use the face with materialIndex == 5
.
Suppose we want to flip both texture parameters? (By “flip”, I mean change 0 to 1 and 1 to 0, which would have the effect of flipping the direction of the texture on this face.) We could compute something like this:
var UV = ...; // index into structure of coords
UV.x = 1-UV.x;
UV.y = 1-UV.y;
Alternatively, if we want to have repetitions on one side, say in a 2x3 pattern, we could do something like this:
var UV = ...; // index into structure of coords
UV.x = 2*UV.x;
UV.y = 3*UV.y;
Notice that if the texture parameter is a zero, multiplying it by 2 or 3 doesn’t affect it, but if it’s a one, multiplying it by 2 or 3 gets us exactly the repetitions we want.
Consequently, we could modify this geometry object, even if it had more segments, with code like this:
var geom = cube.geometry;
var faces = geom.faces;
var UVs = geom.faceVertexUvs[0];
for( var i = 0; i < faces.length; i++ ) {
var face = faces[i];
// modify (s,t) parameters to give 2x3 pattern on front face (4)
if( face.materialIndex == 4 ) {
var faceUV = UVs[i];
// for all three vertices
for( j = 0; j < 3; j++ ) {
var UV = faceUV[j];
UV.x = 2*UV.x;
UV.y = 3*UV.y;
}
}
}
See it in action here:
BoxFlag-v3.html, only we’ve
switched to the US flag, since that makes repetitions more obvious, and we’ve
used the (1-UV.y)
trick to flip the texture vertically.
First, take a look at this demo that Kelsey Reiman built for us a few years ago (rotate the cube with your mouse to see all 6 sides):
Now, take a look at the source code that builds the cube:
TW.loadTextures(
[ 'mikeypics/mikey1.jpg', 'mikeypics/mikey2.jpg',
'mikeypics/mikey3.jpg', 'mikeypics/mikey4.jpg',
'mikeypics/mikey5.jpg', 'mikeypics/mikey6.jpg' ],
function (textures) {
// create an array of materials from these textures
var mats = [];
for( var i=0; i < textures.length; i++ ) {
mats.push(new THREE.MeshBasicMaterial( {map: textures[i]} ));
}
// create a cube using MeshFaceMaterial, one material for each face
var cube = new THREE.Mesh( new THREE.BoxGeometry(2,2,2),
new THREE.MeshFaceMaterial( mats ) );
scene.add(cube);
TW.render();
});
The TW.loadTextures()
function loads an array of images and invokes the
callback once all of them have been loaded.
So, it’s pretty easy to put a different material on each side of a cube.
Of course, as we discovered earlier, this is powerful, but only if you want to do what it makes easy. In particular, you have no control over the texture coordinates for each side. Suppose we want to put repetitions of some image on some side? We’d have to change all the texture parameters that are 1 to a 2, but just for that face. We’ll have to dig in to do that.
You probably won’t be surprised that with THREE.MeshFaceMaterial
, each face
has a materialIndex that indexes into the array of materials, and each
material has its own texture.
Thus, if we wanted to have a 2x3 pattern of Mikey on the front side
(materialIndex == 4)
of the cube, we could use code from above. That’s
actually implemented in the Mikey
Cube
example, bound to the ‘p’ key. Try it!
The code above isn’t horrible, once we get the basic pattern, but it’s not trivial, either. You can see how it might be easier just to launch your favorite graphics editor (PhotoShop or whatever) and create a version of the image for the front of the cube that contains the repetitions you want. There’s certainly nothing wrong or disreputable about that approach. Sometimes, it’s clearly the only approach, if the modification you want is not something that can be done by setting texture parameters.
For this course, I’d prefer you to try to program when you can, and photoshop when you must, but talk to me if you think that a graphics editor is the better way.
So far, we’ve just mapped textures onto plain white surfaces. In fact, the texture is multiplied by the color of the surface (depending on the shader). Consider the following demo:
Now, if the color of the face isn’t direct color (THREE.MeshBasicMaterial
),
but is a function of the material (THREE.MeshPhongMaterial
) and lighting of
the scene, you can easily see how we can combine this lighting information
with a texture.
One question, though, is what color the material should be. You can see that if the material has any hue , it might interact in odd-looking ways with the colors of the texture. Thus, it makes sense for the material to be gray. It will probably be a fairly light shade of gray, maybe even white, since lighting works by multiplying the material by a value less than one, so typically the result is darker than the original. However, it also depends on how many lights are in the scene, since the contributions of all the lights are added up, so colors can also get brighter, even over-driven. So, there’s still some artistic judgment involved.
Consider one last demo:
The trick here is to create a Phong material and then to set the .map
property:
var mat = new THREE.PhongMaterial(); // default is white
mat.map = texture;
Now that we know how to combine material and lighting with texture-mapping, consider what the graphics card is doing. For each pixel, it’s computing the entire Phong Model to yield values for RGB (which will often be all the same, if we have grayscale materials), then multiplying each component by the same component of the texture to yield the color of the pixel.
Food for thought: When might you use colored material and grayscale textures?
In the remaining reading and final lecture on texture-mapping, we’ll discuss:
The last reading included a demo of Buffy texture-mapped onto a plane. Here’s a virtually identical demo showing an image file being loaded and texture- mapped onto the same plane we used before:
However, there’s a hitch with this version that we didn’t have with Buffy, namely the Same-Origin Policy, a security policy in web browsers. That policy covers XMLHttpRequests (Ajax requests) as well, which is where JavaScript code issues the request for the resource. So, even though the browser can request the image from the Harry Potter Wikia, our JavaScript code can’t.
I tried the demo above on all three browsers I have handy on my Mac, and here’s what I get:
Google Chrome (38.0.2125.104)
Uncaught SecurityError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': the cross-origin image at [url] may not be loaded. Firefox (33.0)
Error: WebGL: It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures SecurityError: The operation is insecure.
Safari 6.0.5 (8536.30.1)
Security_ERR: DOM Exception 18: An attempt was made to break through the security policy of the user agent.
A solution could be CORS:
If the site we are loading an image from allows CORS, we should be able to do so by adding an extra header to the request, using the following:
THREE.ImageUtils.crossOrigin = "anonymous"; // or
THREE.ImageUtils.crossOrigin = ""; // the default
However, Scott has not yet been able get this to work, so for now, just keep the following in mind:
Your images have to be on the same computer as your JavaScript program.
The Three.js people are aware of the issue with this Same-Origin Policy, and they also know how nice it is to work locally, as we do on the lab Macs or on our own laptops. In their online documentation, they wrote this nice explanation of how to run things locally.
We will use the “Run a local server” option, using Python. We did this in class, and the essentials are:
cd
to the directory that has your downloaded HTML file in it, such as cd ~/Desktop
.Start a web server on port 8000 (by default) using Python:
python -m SimpleHTTPServer
Go back to your web browser and try the following URL, substituting your HTML filename for the foo.html
http://localhost:8000/foo.html
Here’s what we learned
SimpleHTTPServer
module.THREE.BoxGeometry
and a strategy for modifying the textures by iterating over all the faces, determining which ones we want to modify, and modifying each in a generic way.THREE.FaceMaterials
to map several images onto a single Box. We also learned how we might modify the texture parameters on particular faces by using materialIndex
.This page is based on https://cs.wellesley.edu/~cs307/readings/10-texture-mapping-b.html. Copyright © Scott D. Anderson. This work is licensed under a Creative Commons License.