I have decided to start the new year by taking a closer look at 3D programming for the web via Canvas and WebGL. For those that don’t know, I am trained as a 3D modeler and animator, although I turned from those paths to focus on development. I now feel accomplished enough as a developer to turn to a new page in my career and focus on combining my skills in both disciplines.

It’s a pretty exciting time for the web, In just the past 12 months we have seen massive progress within ‘Html5’ in terms of performance and reach – realities we could only dream of not so long ago. Away3D(stage3D) and Unity3D have had success harnessing GPU accelerated 3D graphics, and while the latter is great for targeting mobile and console, the fact that they require 3rd party plugins (when targeting browser) is notable pitfall.

I’m not big on 3rd party closed source web plugins (especially after what happened with Apple vs Flash).  So, let me introduce you to Three.js.

ThreeJS is a JavaScript API for interfacing with WebGL – pioneered by the legendary Mr Doob and AlteredQualia.  It dramatically simplifies the programmatic creation of 3D experiences.
One of the interesting things about ThreeJS is that you are not bound to a specific renderer. ie. you can create a Canvas renderer (software) or a WebGL renderer (GPU), allowing you to customise your performance and audience reach.

Below I will be showing a simple example of a cube.  Which some considered to be the ‘Hello World’ of the 3D world…
We will start with creating rotating cube with one uniform colour and building on it.

Paste the code below into a new Html document and see the magic begin. I will be updating the post shortly to explain some of the different lines.
(I have tried to comment the important lines, post me a reply if I have not been clear enough!).

<html>
<body>
<script type="text/javascript" src="https://rawgithub.com/mrdoob/three.js/master/build/three.js"></script><script type="text/javascript">// <![CDATA[
//create the scene
var scene = new THREE.Scene();

//create the camera  (field of view, aspect ratio, far clipping plane)
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

//create the renderer
//var renderer = new THREE.WebGLRenderer();
var renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );

//add the renderer to the stage, (renderer returns a <canvas> element)
document.body.appendChild( renderer.domElement );

/* ---------- Add a cube --------------*/

//add the geometry, this is an object that contains all the vertices and faces. pass in the size of the faces
var geometry = new THREE.CubeGeometry(10,10,10);

//add The material
var material = new THREE.MeshBasicMaterial( { color: 0x00baff } );

//create the Mesh using the geometry and material
var cube = new THREE.Mesh( geometry, material );

//add the cube to the scene, defaults to coords 0,0,0 (x,y,z)
scene.add( cube );

//move the camera back a bit so it is not sitting on the cube
// if you see a white canvas try increasing the camera z pos
camera.position.z = 20;

/* ---- Render Loop ---- */
function render()
{
//use requestAnimmationFrame instead of SetInterval
requestAnimationFrame(render);

//update cube rotation
cube.rotation.x += 0.01;
cube.rotation.y += 0.02;

//render
renderer.render(scene, camera);
}

//start the render loop
render();
// ]]></script>

The cube and motion look fine, although having one colour hides the sense of perspective, so let’s modify the code to give the faces different colours.

The first thing we will change is the following line from:

var material = new THREE.MeshBasicMaterial( { color: 0x00baff } );

to:

var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5 } );

This indicates that we will be using coloured faces. You will also see we have added an overdraw parameter. In the first example there is a visible diagnal line on each cube face showing the actual faces of the model – adding an overdraw parameter lets you fine tune the visibility of this line, change the value to see it impacts the material.

Next we need to colour the faces. Directly after the previous line add the following.

				geometry.faces[ 0 ].color.setHex( 0xff0000 );
				geometry.faces[ 1 ].color.setHex( 0xff0000 );

				geometry.faces[ 2 ].color.setHex( 0x00baff);
				geometry.faces[ 3 ].color.setHex( 0x00baff);

				geometry.faces[ 4 ].color.setHex( 0x0000ff);
				geometry.faces[ 5 ].color.setHex( 0x0000ff);

				geometry.faces[ 6 ].color.setHex( 0xff6600);
				geometry.faces[ 7 ].color.setHex( 0xff6600);

				geometry.faces[ 8 ].color.setHex( 0xff00ff);
				geometry.faces[ 9 ].color.setHex( 0xff00ff);

				geometry.faces[ 10 ].color.setHex( 0x91d50e);
				geometry.faces[ 11 ].color.setHex( 0x91d50e);

Note: Each square face is actually made up of 2 triangle faces, so we should set them both to the same colour (unless you want different coloured triangles on your cube).
Or, you can simplify this process and give all the faces random coloures by doing something like this instead.


for ( var i = 0; i < geometry.faces.length; i += 2 )
{
	var hex = Math.random() * 0xffffff;
	geometry.faces[ i ].color.setHex( hex );
	geometry.faces[ i + 1 ].color.setHex( hex );

}

The result should look something like this

And the updated full source:

<html>
<body>
<script type="text/javascript" src="https://rawgithub.com/mrdoob/three.js/master/build/three.js"></script><script type="text/javascript">

	 		//create the scene
			var scene = new THREE.Scene();

			//create the camera   (field of view, aspect ratio, far clipping plane, )
			var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

			//create the renderer
			//var renderer = new THREE.WebGLRenderer();
			var renderer = new THREE.CanvasRenderer();
			renderer.setSize( window.innerWidth, window.innerHeight );

			//add the renderer to the stage, (renderer returns a <canvas> element)
			document.body.appendChild( renderer.domElement );

			/* ---------- Add a cube --------------*/

			//add the geometry, this is an object that contains all the verticies and faces. pass in the size of the faces
			var geometry = new THREE.CubeGeometry(10,10,10);

			//create the material
			var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5 } );

				//give each face a colour, (each square face is made of 2 triangle faces)
				geometry.faces[ 0 ].color.setHex( 0xff0000 );
				geometry.faces[ 1 ].color.setHex( 0xff0000 );

				geometry.faces[ 2 ].color.setHex( 0x00baff);
				geometry.faces[ 3 ].color.setHex( 0x00baff);

				geometry.faces[ 4 ].color.setHex( 0x0000ff);
				geometry.faces[ 5 ].color.setHex( 0x0000ff);

				geometry.faces[ 6 ].color.setHex( 0xff6600);
				geometry.faces[ 7 ].color.setHex( 0xff6600);

				geometry.faces[ 8 ].color.setHex( 0xff00ff);
				geometry.faces[ 9 ].color.setHex( 0xff00ff);

				geometry.faces[ 10 ].color.setHex( 0x91d50e);
				geometry.faces[ 11 ].color.setHex( 0x91d50e);

			/*
				for ( var i = 0; i < geometry.faces.length; i += 2 ) {

					var hex = Math.random() * 0xffffff;
					geometry.faces[ i ].color.setHex( hex );
					geometry.faces[ i + 1 ].color.setHex( hex );

				}
			*/

			//create the Mesh using the geomety and material
			var cube = new THREE.Mesh( geometry, material );

			//add tge cube to the scene, defaults to coords 0,0,0 (x,y,z)
			scene.add( cube );

			//move the camera back a bit so it is not sitting on the cube, if you see a white canvas try adjusting the camera pos
			camera.position.z = 20;

			/* ---- Render Loop ---- */
			function render()
			{
			//use requestAnimmationFrame instead of SetInterval
			 requestAnimationFrame(render);

			 //update cube rotation
			 cube.rotation.x += 0.01;
			 cube.rotation.y += 0.02;

			 //render
			 renderer.render(scene, camera);
			}

			//start the render loop
			render();

// ]]></script>

The final part of this tutorial will demonstrate how to add a classic crate texture map to the cube.

And finally the source for the textured cube.
Note that I have switched the renderer to WebGL instead of canvas, this is because WebGL handles textures much better than the Canvas renderer.
download the crate texture here
Keep in mind when creating your own textures, to keep them in power of two’s (128×128, 256×256, 512×512 etc), this allows for easier loading onto the GPU.

<html>

 <head>
   <style>canvas { width: 100%; height: 100% }</style>
</head>

	 <body>

	 <script src="https://rawgithub.com/mrdoob/three.js/master/build/three.js"></script>

	 <script>
	 		//create the scene
			var scene = new THREE.Scene();

			//create the camera   (field of view, aspect ratio, far clipping plane, )
			var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

			//create the renderer
			var renderer = new THREE.WebGLRenderer();
			//var renderer = new THREE.CanvasRenderer();
			renderer.setSize( window.innerWidth, window.innerHeight );
                        //set the background colour of the renderer
                        renderer.setClearColor(0xffffff, 0);

			//add the renderer to the stage, (renderer returns a <canvas> element)
			document.body.appendChild( renderer.domElement );

			/* ---------- Add a cube --------------*/

			//add the geometry, this is an object that contains all the verticies and faces. pass in the size of the faces
			var geometry = new THREE.CubeGeometry(10,10,10);

			//create the texture by loading our image from file
			var texture = THREE.ImageUtils.loadTexture( 'textures/crate.jpg' );

			//set quality of the texture when it is viewed on a perspective (not essential)
			texture.anisotropy = renderer.getMaxAnisotropy();

			var material = new THREE.MeshBasicMaterial( { map: texture } );

			//create the Mesh using the geometry and material
			var cube = new THREE.Mesh( geometry, material );

			//add the cube to the scene, defaults to coords 0,0,0 (x,y,z)
			scene.add( cube );

			//move the camera back a bit so it is not sitting on the cube, if you see a white canvas try adjusting the camera pos
			camera.position.z = 20;

			/* ---- Render Loop ---- */
			function render()
			{
			//use requestAnimmationFrame instead of SetInterval
			 requestAnimationFrame(render);

			 //update cube rotation
			 cube.rotation.x += 0.01;
			 cube.rotation.y += 0.02;

			 //render
			 renderer.render(scene, camera);
			}

			//start the render loop
			render();

	 </script>

	</body>

</html>

I hope you have found this post useful. I think 3D programming in WebGL/Canvas is currently the most exciting area of the industry, so I will try to put more focus on it in my posts, stay tuned.