Per Brage's Blog

const String ABOUT = "Somehow related to code";

HTML5/JavaScript Cube (Part 2: Improving with Three.js)

In part one I showed you how to create a spinning cube by building a mesh, and then applying basic linear algebra to make it rotate. The development was fun in itself but looking at the surrounding world, and what happened the past 15 years or so, evolution definitely leaped forward. These days there are tons of 3D engines, frameworks, libraries and what not, so there is really no need to do all that work yourself. As it happens to be, there is this JavaScript 3D library named Three.js, which enables you to create stunning 3D graphics with a small amount of effort. I thought it would be fun to see if I could improve my cube by refactoring it to use Three.js, and at the same time add some new features like texture mapping and light sources.

However, when adding features like texture mapping there is a rather large issue with browser compatibility that presents itself, which I think requires a bit of explanation. Three.js has three different main renderers; CanvasRenderer, SVGRenderer and WebGLRenderer (with some variations of WebGLRenderers). They all have a few pros and cons, but I’m focusing on the problems related to my features.

  • CanvasRenderer
  • Works on all major browsers since its just a HTML5 Canvas and JavaScript. The bad part is that the texture mapping routine is not perspective correct, and there is no interpolation in the triangle drawing routine, which produces gaps between aligned polygons. And when I tested it, I couldn’t even get the texture mapping to work in IE9.

  • SVGRenderer
  • Doesn’t support texture mapping at all, so that is a no go, at least for the new features I want to add.

  • WebGLRenderer
  • The best of all three renderers, but the problem is that WebGL is not supported by all major browsers, especially IE. It seems that people are still using IE for some unknown reasons, but in my opinion they should just remove the 6 out of http://www.ie6countdown.com/. Even so, I’m not sure IE will ever support WebGL since it’s based on OpenGL, which is a direct competitor to Microsoft DirectX. Microsoft also openly stated that WebGL is insecure, as it directly exposes hardware functionality to the web. I guess they may have a point there!

This creates a problem with Three.js, which is that there is not a single solution that allows me to texture map a cube and have it work on all major browsers (speaking of recent versions). For the sake of this post and the challenge I started, I will use WebGLRenderer and strictly require Chrome and Firefox browsers to use it. So if you can’t click and see the demo below, switch to a real browser.

Refactoring to use Three.js

In my new cube I have reduced the lines of code from about 250 lines, to 90 lines of code (not minified JavaScript). And the features I wanted to add, texture mapping and light sources was of course provided by Three.js out of the box. The code, when refactored turned into a few simple steps; create renderer and a scene, add a camera, a cube and then just add our light sources to the scene. Still, I have to take care of the actual spinning myself, but that can now be done without any linear algebra.

Here are the few very simple functions that replaces most of my code from part one.


var buildCamera = function () {
    var camera = new THREE.PerspectiveCamera(50, viewPortWidth / viewPortHeight, 1, 1000);
    camera.position.z = 500;
    return camera;
};

var buildCube = function () {
    var cubeGeometry = new THREE.CubeGeometry(200, 200, 200, 1, 1, 1, buildMaterials());
    return new THREE.Mesh(cubeGeometry, new THREE.MeshFaceMaterial());
};

var buildDirectionalLight = function () {
    var directionalLight = new THREE.DirectionalLight(0xffffff);
    directionalLight.position.set(1, 1, 1).normalize();
    return directionalLight;
};

var buildAmbientLight = function () {
    return new THREE.AmbientLight(0x999999);
};

The refresh function is similar as before, but Three.js takes care of the drawing now. So the refresh function was refactored into a calculateCube function that increases the angle, and then sets the angle on the cubes rotation properties before it renders the scene.


var calculateCube = function (cube, scene, camera) {
    angle++;
    cube.rotation.x = degreesToRadians(angle);
    cube.rotation.y = degreesToRadians(angle);
    cube.rotation.z = degreesToRadians(angle);
    renderer.render(scene, camera);
};

Now we simply add the output of each and every function above to the scene, and then start a timer to call our calculateCube function.

        var scene = new THREE.Scene();
        var camera = buildCamera();
        var cube = buildCube();

        scene.add(camera);
        scene.add(cube);
        scene.add(buildAmbientLight());
        scene.add(buildDirectionalLight());

        setInterval(function () {
            calculateCube(cube, scene, camera);
        }, 20);

Result

threejscubeResult

Links

Live demo of spinning cube using Three.js
Full source at my blog-examples repository on GitHub

Demonstrations of the Three.js library.

Advertisements

One response to “HTML5/JavaScript Cube (Part 2: Improving with Three.js)

  1. Pranav December 2, 2012 at 09:06

    Thanks a ton for the tip on ambient light! I had been struggling with it for the last couple of days! Interestingly, I needn’t add ambient light to THREE.js release 50 and prior.

    Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: