Working with Blender

In this article by Jos Dirksen, author of Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition, we will learn about Blender and also about how to load models in Three.js using different formats.

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

Before we get started with the configuration, we'll show the result that we'll be aiming for. In the following screenshot, you can see a simple Blender model that we exported with the Three.js plugin and imported in Three.js with THREE.JSONLoader:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

Installing the Three.js exporter in Blender

To get Blender to export Three.js models, we first need to add the Three.js exporter to Blender. The following steps are for Mac OS X but are pretty much the same on Windows and Linux. You can download Blender from www.blender.org and follow the platform-specific installation instructions. After installation, you can add the Three.js plugin. First, locate the addons directory from your Blender installation using a terminal window:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

On my Mac, it's located here: ./blender.app/Contents/MacOS/2.70/scripts/addons. For Windows, this directory can be found at the following location: C:\Users\USERNAME\AppData\Roaming\Blender Foundation\Blender\2.7X\scripts\addons. And for Linux, you can find this directory here: /home/USERNAME/.config/blender/2.7X/scripts/addons.

Next, you need to get the Three.js distribution and unpack it locally. In this distribution, you can find the following folder: utils/exporters/blender/2.65/scripts/addons/. In this directory, there is a single subdirectory with the name io_mesh_threejs. Copy this directory to the addons folder of your Blender installation.

Now, all we need to do is start Blender and enable the exporter. In Blender, open Blender User Preferences (File | User Preferences). In the window that opens, select the Addons tab, and in the search box, type three. This will show the following screen:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

At this point, the Three.js plugin is found, but it is still disabled. Check the small checkbox to the right, and the Three.js exporter will be enabled. As a final check to see whether everything is working correctly, open the File | Export menu option, and you'll see Three.js listed as an export option. This is shown in the following screenshot:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

With the plugin installed, we can load our first model.

Loading and exporting a model from Blender

As an example, we've added a simple Blender model named misc_chair01.blend in the assets/models folder, which you can find in the sources for this article. In this section, we'll load this model and show the minimal steps it takes to export this model to Three.js.

First, we need to load this model in Blender. Use File | Open and navigate to the folder containing the misc_chair01.blend file. Select this file and click on Open. This will show you a screen that looks somewhat like this:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

Exporting this model to the Three.js JSON format is pretty straightforward. From the File menu, open Export | Three.js, type in the name of the export file, and select Export Three.js. This will create a JSON file in a format Three.js understands. A part of the contents of this file is shown next:

{
 
"metadata" :
{
   "formatVersion" : 3.1,
   "generatedBy"   : "Blender 2.7 Exporter",
   "vertices"     : 208,
   "faces"         : 124,
   "normals"       : 115,
   "colors"       : 0,
   "uvs"          : [270,151],
   "materials"     : 1,
   "morphTargets" : 0,
   "bones"         : 0
},
...

However, we aren't completely done. In the previous screenshot, you can see that the chair contains a wooden texture. If you look through the JSON export, you can see that the export for the chair also specifies a material, as follows:

"materials": [{
"DbgColor": 15658734,
"DbgIndex": 0,
"DbgName": "misc_chair01",
"blending": "NormalBlending",
"colorAmbient": [0.53132, 0.25074, 0.147919],
"colorDiffuse": [0.53132, 0.25074, 0.147919],
"colorSpecular": [0.0, 0.0, 0.0],
"depthTest": true,
"depthWrite": true,
"mapDiffuse": "misc_chair01_col.jpg",
"mapDiffuseWrap": ["repeat", "repeat"],
"shading": "Lambert",
"specularCoef": 50,
"transparency": 1.0,
"transparent": false,
"vertexColors": false
}],

This material specifies a texture, misc_chair01_col.jpg, for the mapDiffuse property. So, besides exporting the model, we also need to make sure the texture file is also available to Three.js. Luckily, we can save this texture directly from Blender.

In Blender, open the UV/Image Editor view. You can select this view from the drop-down menu on the left-hand side of the File menu option. This will replace the top menu with the following:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

Make sure the texture you want to export is selected, misc_chair_01_col.jpg in our case (you can select a different one using the small image icon). Next, click on the Image menu and use the Save as Image menu option to save the image. Save it in the same folder where you saved the model using the name specified in the JSON export file. At this point, we're ready to load the model into Three.js.

The code to load this into Three.js at this point looks like this:

var loader = new THREE.JSONLoader();
loader.load('../assets/models/misc_chair01.js', function (geometry, mat) {
mesh = new THREE.Mesh(geometry, mat[0]);
 
mesh.scale.x = 15;
mesh.scale.y = 15;
mesh.scale.z = 15;
 
scene.add(mesh);
 
}, '../assets/models/');

We've already seen JSONLoader before, but this time, we use the load function instead of the parse function. In this function, we specify the URL we want to load (points to the exported JSON file), a callback that is called when the object is loaded, and the location, ../assets/models/, where the texture can be found (relative to the page). This callback takes two parameters: geometry and mat. The geometry parameter contains the model, and the mat parameter contains an array of material objects. We know that there is only one material, so when we create THREE.Mesh, we directly reference that material. If you open the 05-blender-from-json.html example, you can see the chair we just exported from Blender.

Using the Three.js exporter isn't the only way of loading models from Blender into Three.js. Three.js understands a number of 3D file formats, and Blender can export in a couple of those formats. Using the Three.js format, however, is very easy, and if things go wrong, they are often quickly found.

In the following section, we'll look at a couple of the formats Three.js supports and also show a Blender-based example for the OBJ and MTL file formats.

Importing from 3D file formats

At the beginning of this article, we listed a number of formats that are supported by Three.js. In this section, we'll quickly walk through a couple of examples for those formats. Note that for all these formats, an additional JavaScript file needs to be included. You can find all these files in the Three.js distribution in the examples/js/loaders directory.

The OBJ and MTL formats

OBJ and MTL are companion formats and often used together. The OBJ file defines the geometry, and the MTL file defines the materials that are used. Both OBJ and MTL are text-based formats. A part of an OBJ file looks like this:

v -0.032442 0.010796 0.025935
v -0.028519 0.013697 0.026201
v -0.029086 0.014533 0.021409
usemtl Material
s 1
f 2731 2735 2736 2732
f 2732 2736 3043 3044

The MTL file defines materials like this:

newmtl Material
Ns 56.862745
Ka 0.000000 0.000000 0.000000
Kd 0.360725 0.227524 0.127497
Ks 0.010000 0.010000 0.010000
Ni 1.000000
d 1.000000
illum 2

The OBJ and MTL formats by Three.js are understood well and are also supported by Blender. So, as an alternative, you could choose to export models from Blender in the OBJ/MTL format instead of the Three.js JSON format. Three.js has two different loaders you can use. If you only want to load the geometry, you can use OBJLoader. We used this loader for our example (06-load-obj.html). The following screenshot shows this example:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

To import this in Three.js, you have to add the OBJLoader JavaScript file:

<script type="text/javascript" src="../libs/OBJLoader.js"> </script>

Import the model like this:

var loader = new THREE.OBJLoader();
loader.load('../assets/models/pinecone.obj', function (loadedMesh) {
var material = new THREE.MeshLambertMaterial({color: 0x5C3A21});
 
// loadedMesh is a group of meshes. For
// each mesh set the material, and compute the information
// three.js needs for rendering.
loadedMesh.children.forEach(function (child) {
   child.material = material;
   child.geometry.computeFaceNormals();
   child.geometry.computeVertexNormals();
});
 
mesh = loadedMesh;
loadedMesh.scale.set(100, 100, 100);
loadedMesh.rotation.x = -0.3;
scene.add(loadedMesh);
});

In this code, we use OBJLoader to load the model from a URL. Once the model is loaded, the callback we provide is called, and we add the model to the scene.

Usually, a good first step is to print out the response from the callback to the console to understand how the loaded object is built up. Often with these loaders, the geometry or mesh is returned as a hierarchy of groups. Understanding this makes it much easier to place and apply the correct material and take any other additional steps. Also, look at the position of a couple of vertices to determine whether you need to scale the model up or down and where to position the camera. In this example, we've also made the calls to computeFaceNormals and computeVertexNormals. This is required to ensure that the material used (THREE.MeshLambertMaterial) is rendered correctly.

The next example (07-load-obj-mtl.html) uses OBJMTLLoader to load a model and directly assign a material. The following screenshot shows this example:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

First, we need to add the correct loaders to the page:

<script type="text/javascript" src="../libs/OBJLoader.js"> </script>
<script type="text/javascript" src="../libs/MTLLoader.js"> </script>
<script type="text/javascript" src="../libs/OBJMTLLoader.js"> </script>

We can load the model from the OBJ and MTL files like this:

var loader = new THREE.OBJMTLLoader();
loader.load('../assets/models/butterfly.obj', '../assets/ models/butterfly.mtl', function(object) {
// configure the wings
var wing2 = object.children[5].children[0];
var wing1 = object.children[4].children[0];
 
wing1.material.opacity = 0.6;
wing1.material.transparent = true;
wing1.material.depthTest = false;
wing1.material.side = THREE.DoubleSide;
 
wing2.material.opacity = 0.6;
wing2.material.depthTest = false;
wing2.material.transparent = true;
wing2.material.side = THREE.DoubleSide;
 
object.scale.set(140, 140, 140);
mesh = object;
scene.add(mesh);
 
mesh.rotation.x = 0.2;
mesh.rotation.y = -1.3;
});

The first thing to mention before we look at the code is that if you receive an OBJ file, an MTL file, and the required texture files, you'll have to check how the MTL file references the textures. These should be referenced relative to the MTL file and not as an absolute path. The code itself isn't that different from the one we saw for THREE.ObjLoader. We specify the location of the OBJ file, the location of the MTL file, and the function to call when the model is loaded. The model we've used as an example in this case is a complex model. So, we set some specific properties in the callback to fix some rendering issues, as follows:

  • The opacity in the source files was set incorrectly, which caused the wings to be invisible. So, to fix that, we set the opacity and transparent properties ourselves.
  • By default, Three.js only renders one side of an object. Since we look at the wings from two sides, we need to set the side property to the THREE.DoubleSide value.
  • The wings caused some unwanted artifacts when they needed to be rendered on top of each other. We've fixed that by setting the depthTest property to false. This has a slight impact on performance but can often solve some strange rendering artifacts.

But, as you can see, you can easily load complex models directly into Three.js and render them in real time in your browser. You might need to fine-tune some material properties though.

Loading a Collada model

Collada models (extension is .dae) are another very common format for defining scenes and models (and animations as well). In a Collada model, it is not just the geometry that is defined, but also the materials. It's even possible to define light sources.

To load Collada models, you have to take pretty much the same steps as for the OBJ and MTL models. You start by including the correct loader:

<script type="text/javascript" src="../libs/ColladaLoader.js"> </script>

For this example, we'll load the following model:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

Loading a truck model is once again pretty simple:

var mesh;
loader.load("../assets/models/dae/Truck_dae.dae", function   (result) {
mesh = result.scene.children[0].children[0].clone();
mesh.scale.set(4, 4, 4);
scene.add(mesh);
});

The main difference here is the result of the object that is returned to the callback. The result object has the following structure:

var result = {
 
scene: scene,
morphs: morphs,
skins: skins,
animations: animData,
dae: {
   ...
}
};

In this article, we're interested in the objects that are in the scene parameter. I first printed out the scene to the console to look where the mesh was that I was interested in, which was result.scene.children[0].children[0]. All that was left to do was scale it to a reasonable size and add it to the scene. A final note on this specific example—when I loaded this model for the first time, the materials didn't render correctly. The reason was that the textures used the .tga format, which isn't supported in WebGL. To fix this, I had to convert the .tga files to .png and edit the XML of the .dae model to point to these .png files.

As you can see, for most complex models, including materials, you often have to take some additional steps to get the desired results. By looking closely at how the materials are configured (using console.log()) or replacing them with test materials, problems are often easy to spot.

Loading the STL, CTM, VTK, AWD, Assimp, VRML, and Babylon models

We're going to quickly skim over these file formats as they all follow the same principles:

  1. Include [NameOfFormat]Loader.js in your web page.
  2. Use [NameOfFormat]Loader.load() to load a URL.
  3. Check what the response format for the callback looks like and render the result.

We have included an example for all these formats:

Name

Example

Screenshot

STL

08-load-STL.html

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

CTM

09-load-CTM.html

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

VTK

10-load-vtk.html

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

AWD

11-load-awd.html

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

Assimp

12-load-assimp.html

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

VRML

13-load-vrml.html

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

Babylon

The Babylon loader is slightly different from the other loaders in this table. With this loader, you don't load a single THREE.Mesh or THREE.Geometry instance, but with this loader, you load a complete scene, including lights.

 

14-load-babylon.html

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

If you look at the source code for these examples, you might see that for some of them, we need to change some material properties or do some scaling before the model is rendered correctly. The reason we need to do this is because of the way the model is created in its external application, giving it different dimensions and grouping than we normally use in Three.js.

Summary

In this article, we've almost shown all the supported file formats. Using models from external sources isn't that hard to do in Three.js. Especially for simple models, you only have to take a few simple steps. When working with external models, or creating them using grouping and merging, it is good to keep a couple of things in mind. The first thing you need to remember is that when you group objects, they still remain available as individual objects. Transformations applied to the parent also affect the children, but you can still transform the children individually. Besides grouping, you can also merge geometries together. With this approach, you lose the individual geometries and get a single new geometry. This is especially useful when you're dealing with thousands of geometries you need to render and you're running into performance issues.

Three.js supports a large number of external formats. When using these format loaders, it's a good idea to look through the source code and log out the information received in the callback. This will help you to understand the steps you need to take to get the correct mesh and set it to the correct position and scale. Often, when the model doesn't show correctly, this is caused by its material settings. It could be that incompatible texture formats are used, opacity is incorrectly defined, or the format contains incorrect links to the texture images. It is usually a good idea to use a test material to determine whether the model itself is loaded correctly and log the loaded material to the JavaScript console to check for unexpected values. It is also possible to export meshes and scenes, but remember that GeometryExporter, SceneExporter, and SceneLoader of Three.js are still work in progress.

Resources for Article:


Further resources on this subject:


You've been reading an excerpt of:

Learning Three.js – the JavaScript 3D Library for WebGL - Second Edition

Explore Title