11 min read

In this article by Jos Dirksen author of the book Three.js Cookbook, we will learn how Three.js offers a large number of different materials and supports many different types of textures. These textures provide a great way to create interesting effects and graphics. In this article, we’ll show you recipes that allow you to get the most out of these components provided by Three.js.

(For more resources related to this topic, see here.)

Using HTML canvas as a texture

Most often when you use textures, you use static images. With Three.js, however, it is also possible to create interactive textures. In this recipe, we will show you how you can use an HTML5 canvas element as an input for your texture. Any change to this canvas is automatically reflected after you inform Three.js about this change in the texture used on the geometry.

Getting ready

For this recipe, we need an HTML5 canvas element that can be displayed as a texture. We can create one ourselves and add some output, but for this recipe, we’ve chosen something else. We will use a simple JavaScript library, which outputs a clock to a canvas element. The resulting mesh will look like this (see the 04.03-use-html-canvas-as-texture.html example):

The JavaScript used to render the clock was based on the code from this site: http://saturnboy.com/2013/10/html5-canvas-clock/. To include the code that renders the clock in our page, we need to add the following to the head element:

<script src="../libs/clock.js"></script>

How to do it…

To use a canvas as a texture, we need to perform a couple of steps:

  1. The first thing we need to do is create the canvas element:
    var canvas = document.createElement('canvas');
    canvas.width=512;
    canvas.height=512;

    Here, we create an HTML canvas element programmatically and define a fixed width.

  2. Now that we’ve got a canvas, we need to render the clock that we use as the input for this recipe on it. The library is very easy to use; all you have to do is pass in the canvas element we just created:
    clock(canvas);
  3. At this point, we’ve got a canvas that renders and updates an image of a clock. What we need to do now is create a geometry and a material and use this canvas element as a texture for this material:
    var cubeGeometry = new THREE.BoxGeometry(10, 10, 10);
    var cubeMaterial = new THREE.MeshLambertMaterial();
    cubeMaterial.map = new THREE.Texture(canvas);
    var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);

    To create a texture from a canvas element, all we need to do is create a new instance of THREE.Texture and pass in the canvas element we created in step 1. We assign this texture to the cubeMaterial.map property, and that’s it.

  4. If you run the recipe at this step, you might see the clock rendered on the sides of the cubes. However, the clock won’t update itself. We need to tell Three.js that the canvas element has been changed. We do this by adding the following to the rendering loop:
    cubeMaterial.map.needsUpdate = true;

    This informs Three.js that our canvas texture has changed and needs to be updated the next time the scene is rendered.

With these four simple steps, you can easily create interactive textures and use everything you can create on a canvas element as a texture in Three.js.

How it works…

How this works is actually pretty simple. Three.js uses WebGL to render scenes and apply textures. WebGL has native support for using HTML canvas element as textures, so Three.js just passes on the provided canvas element to WebGL and it is processed as any other texture.

Making part of an object transparent

You can create a lot of interesting visualizations using the various materials available with Three.js. In this recipe, we’ll look at how you can use the materials available with Three.js to make part of an object transparent. This will allow you to create complex-looking geometries with relative ease.

Getting ready

Before we dive into the required steps in Three.js, we first need to have the texture that we will use to make an object partially transparent. For this recipe, we will use the following texture, which was created in Photoshop:

You don’t have to use Photoshop; the only thing you need to keep in mind is that you use an image with a transparent background. Using this texture, in this recipe, we’ll show you how you can create the following (04.08-make-part-of-object-transparent.html):

As you can see in the preceeding, only part of the sphere is visible, and you can look through the sphere to see the back at the other side of the sphere.

How to do it…

Let’s look at the steps you need to take to accomplish this:

  1. The first thing we do is create the geometry. For this recipe, we use THREE.SphereGeometry:
    var sphereGeometry = new THREE.SphereGeometry(6, 20, 20);

    Just like all the other recipes, you can use whatever geometry you want.

  2. In the second step, we create the material:
    var mat = new THREE.MeshPhongMaterial();
    mat.map = new THREE.ImageUtils.loadTexture(
             "../assets/textures/partial-transparency.png");
    mat.transparent = true;
    mat.side = THREE.DoubleSide;
    mat.depthWrite = false;
    mat.color = new THREE.Color(0xff0000);

    As you can see in this fragment, we create THREE.MeshPhongMaterial and load the texture we saw in the Getting ready section of this recipe. To render this correctly, we also need to set the side property to THREE.DoubleSide so that the inside of the sphere is also rendered, and we need to set the depthWrite property to false. This will tell WebGL that we still want to test our vertices against the WebGL depth buffer, but we don’t write to it. Often, you need to set this to false when working with more complex transparent objects or particles.

  3. Finally, add the sphere to the scene:
    var sphere = new THREE.Mesh(sphereGeometry, mat);
    scene.add(sphere);

With these simple steps, you can create really interesting effects by just experimenting with textures and geometries.

There’s more

With Three.js, it is possible to repeat textures (refer to the Setup repeating textures recipe). You can use this to create interesting-looking objects such as this:

The code required to set a texture to repeat is the following:

var mat = new THREE.MeshPhongMaterial();
mat.map = new THREE.ImageUtils.loadTexture(
               "../assets/textures/partial-transparency.png");
mat.transparent = true;
mat.map.wrapS = mat.map.wrapT = THREE.RepeatWrapping;
mat.map.repeat.set( 4, 4 );
mat.depthWrite = false;
mat.color = new THREE.Color(0x00ff00);

By changing the mat.map.repeat.set values, you define how often the texture is repeated.

Using a cubemap to create reflective materials

With the approach Three.js uses to render scenes in real time, it is difficult and very computationally intensive to create reflective materials. Three.js, however, provides a way you can cheat and approximate reflectivity. For this, Three.js uses cubemaps. In this recipe, we’ll explain how to create cubemaps and use them to create reflective materials.

Getting ready

A cubemap is a set of six images that can be mapped to the inside of a cube. They can be created from a panorama picture and look something like this:

In Three.js, we map such a map on the inside of a cube or sphere and use that information to calculate reflections. The following screenshot (example 04.10-use-reflections.html) shows what this looks like when rendered in Three.js:

As you can see in the preceeding screenshot, the objects in the center of the scene reflect the environment they are in. This is something often called a skybox. To get ready, the first thing we need to do is get a cubemap. If you search on the Internet, you can find some ready-to-use cubemaps, but it is also very easy to create one yourself. For this, go to http://gonchar.me/panorama/. On this page, you can upload a panoramic picture and it will be converted to a set of pictures you can use as a cubemap. For this, perform the following steps:

  1. First, get a 360 degrees panoramic picture. Once you have one, upload it to the http://gonchar.me/panorama/ website by clicking on the large OPEN button: 

  2. Once uploaded, the tool will convert the panorama picture to a cubemap as shown in the following screenshot: 

  3. When the conversion is done, you can download the various cube map sites. The recipe in this book uses the naming convention provided by Cube map sides option, so download them. You’ll end up with six images with names such as right.png, left.png, top.png, bottom.png, front.png, and back.png.

Once you’ve got the sides of the cubemap, you’re ready to perform the steps in the recipe.

How to do it…

To use the cubemap we created in the previous section and create reflecting material,we need to perform a fair number of steps, but it isn’t that complex:

  1. The first thing you need to do is create an array from the cubemap images you downloaded:
    var urls = [
        '../assets/cubemap/flowers/right.png',
        '../assets/cubemap/flowers/left.png',
        '../assets/cubemap/flowers/top.png',
        '../assets/cubemap/flowers/bottom.png',
        '../assets/cubemap/flowers/front.png',
        '../assets/cubemap/flowers/back.png'
    ];
  2. With this array, we can create a cubemap texture like this:
    var cubemap = THREE.ImageUtils.loadTextureCube(urls);
    cubemap.format = THREE.RGBFormat;
  3. From this cubemap, we can use THREE.BoxGeometry and a custom THREE.ShaderMaterial object to create a skybox (the environment surrounding our meshes):
    var shader = THREE.ShaderLib[ "cube" ];
    shader.uniforms[ "tCube" ].value = cubemap;
    
    var material = new THREE.ShaderMaterial( {
    
        fragmentShader: shader.fragmentShader,
        vertexShader: shader.vertexShader,
        uniforms: shader.uniforms,
        depthWrite: false,
        side: THREE.DoubleSide
    
    });
    
    // create the skybox
    var skybox = new THREE.Mesh( new THREE.BoxGeometry( 10000, 10000, 10000 ), material );
    scene.add(skybox);

    Three.js provides a custom shader (a piece of WebGL code) that we can use for this. As you can see in the code snippet, to use this WebGL code, we need to define a THREE.ShaderMaterial object. With this material, we create a giant THREE.BoxGeometry object that we add to scene.

  4. Now that we’ve created the skybox, we can define the reflecting objects:
    var sphereGeometry = new THREE.SphereGeometry(4,15,15);
    var envMaterial = new THREE.MeshBasicMaterial(
                                     {envMap:cubemap});
    var sphere = new THREE.Mesh(sphereGeometry, envMaterial);

    As you can see, we also pass in the cubemap we created as a property (envmap) to the material. This informs Three.js that this object is positioned inside a skybox, defined by the images that make up cubemap.

  5. The last step is to add the object to the scene, and that’s it:
    scene.add(sphere);

In the example in the beginning of this recipe, you saw three geometries. You can use this approach with all different types of geometries. Three.js will determine how to render the reflective area.

How it works…

Three.js itself doesn’t really do that much to render the cubemap object. It relies on a standard functionality provided by WebGL. In WebGL, there is a construct called samplerCube. With samplerCube, you can sample, based on a specific direction, which color matches the cubemap object. Three.js uses this to determine the color value for each part of the geometry. The result is that on each mesh, you can see a reflection of the surrounding cubemap using the WebGL textureCube function. In Three.js, this results in the following call (taken from the WebGL shader in GLSL):

vec4 cubeColor = textureCube( tCube, 
                 vec3( -vReflect.x, vReflect.yz ) );

A more in-depth explanation on how this works can be found at http://codeflow.org/entries/2011/apr/18/advanced-webgl-part-3-irradiance-environment-map/#cubemap-lookup.

There’s more…

In this recipe, we created the cubemap object by providing six separate images. There is, however, an alternative way to create the cubemap object. If you’ve got a 360 degrees panoramic image, you can use the following code to directly create a cubemap object from that image:

var texture = THREE.ImageUtils.loadTexture( 360-degrees.png',
              new THREE.UVMapping());

Normally when you create a cubemap object, you use the code shown in this recipe to map it to a skybox. This usually gives the best results but requires some extra code. You can also use THREE.SphereGeometry to create a skybox like this:

var mesh = new THREE.Mesh( 
          new THREE.SphereGeometry( 500, 60, 40 ), 
          new THREE.MeshBasicMaterial( { map: texture }));
mesh.scale.x = -1;

This applies the texture to a sphere and with mesh.scale, turns this sphere inside out.

Besides reflection, you can also use a cubemap object for refraction (think about light bending through water drops or glass objects):

All you have to do to make a refractive material is load the cubemap object like this:

var cubemap = THREE.ImageUtils.loadTextureCube(urls, new THREE.CubeRefractionMapping());

And define the material in the following way:

var envMaterial = new THREE.MeshBasicMaterial({envMap:cubemap});
envMaterial.refractionRatio = 0.95;

Summary

In this article, we learned about the different textures and materials supported by Three.js

Resources for Article:

 Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here