Adobe Creative Cloud

April 14, 2016 /Code /

3D in the browser with Three.JS

In this tutorial we’ll get started into making 3D graphics in the browser, using WebGL and three.js.

So what’s WebGL? What’s three.js? Three.js is a library “that makes 3D in the browser easy to use”. For rendering, it uses WebGL (among other options), which is an API that allows the use of the GPU and provides great performance for real-time graphics. That allows us to make games, visualizations, effects, etc, all inside the browser. Support for it is very good by now, too!

See the Pen [under construction] by Lucas Bebber (@lbebber) on CodePen.

Getting started

You can download three.js in the project’s page. It seems like a pretty hefty package for a JS library (~160mb), but most of that size is taken by a large examples folder, which will prove to be very useful when you want to explore the possibilities of the library on your own. To actually use three.js on a project, all you have to do is to include in the HTML the three.min.js file bundled.

For this article, you can try the library on Codepen. Create a new pen and add three.js as an external script.

codepen1

codepen2

 

codepen3

Close the dialog and we’re good to go.

Setup

The basic things we need in order to render something are: a scene, a camera, and a renderer, besides of course the actual objects to be rendered.

A scene is where you put everything that is a 3D “thing”: objects, lights, etc. You can think of it as the 3D world.

Creating a scene is very simple:

var scene = new THREE.Scene();

The renderer‘s purpose is twofold: it contains the DOM element that will be actually inserted in the page (a canvas element), and it, well, renders the image.

Here is how you use it:

// Create it
var renderer = new THREE.WebGLRenderer();
// Set its dimensions - in this case, the size of the window
renderer.setSize( window.innerWidth, window.innerHeight );
// Insert it on the document
document.body.appendChild( renderer.domElement );

The camera‘s main purpose is to transform 3D coordinates into 2D, screen coordinates. You use it much like a real life camera – point it to the object you want to see.

var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

It has a couple of parameters, so let me explain what each of them are:

The first parameter is the field of view – how wide the camera’s angle of vision is. The wider it gets, the larger the visible area is, but things get smaller and more distorted.

The second parameter is the aspect ratio. It usually should be the width of the renderer divided by its height, otherwise things will get squished.

The last two parameters are the near and the far clipping planes – that is, how close and how far an object must be before it gets ignored by the renderer.


To tie it all together, we tell the renderer that we want to render the scene with the camera:

renderer.render(scene,camera);

Of course, since we haven’t added anything on the scene so far, the rendering will only produce a black rectangle.

See the Pen JXNedr by Lucas Bebber (@lbebber) on CodePen.

Objects

Now we need an object to be rendered.

There are several types of object, but the most common is the Mesh object, which consists of a (polygon mesh). You can load that mesh from a 3D file or generate it from a Geometry, which is an abstract object that describes, well, geometric shapes. Then, you have to apply a Material to it. Materials describe the appearance of an object – its color, shininess, how it reacts to light, etc.

So, let’s create a box. First, we create the geometry:

var boxGeometry = new THREE.BoxGeometry(1,1,1);

Those three parameters describe the box’s dimensions: its width, height and depth.

Next, we create the material. It will be a solid color object for now.

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

The MeshBasicMaterial applies a solid color to an object regardless of illumination. This is good for, among other things, debugging.

Then, we’ll make a Mesh using both the geometry and the material we just created:

var box = new THREE.Mesh(boxGeometry, material);

Finally, we add the box to the scene:

scene.add(box);

We have a very small problem here: both the camera and the box are at the same default position (coordinates 0,0,0), and so our box won´t be visible from the camera’s point of view. So let’s back the camera off a bit:

camera.position.z=2;

Please note that all of this must be done before we tell the renderer to render.

Now let’s see what we have so far:

See the Pen mPmQXg by Lucas Bebber (@lbebber) on CodePen.

Not very exciting, but now we’re seeing something!

Now let’s make use of the fact that we’re working with 3D after all and rotate our box along the Y axis.

box.rotation.y=0.5; // in radians

See the Pen XdROpW by Lucas Bebber (@lbebber) on CodePen.

Looks like it’s working, but the flatness of the color is not much interesting. So we’ll look into…

Lights

To make a shaded object, we need both a light and a material that supports its illumination. On three.js, we’ve got several options of lights and two options of materials for that. In this article, I’m going to use the PointLight and the MeshPhongMaterial.

PointLight is a pretty simple light to use – as its name suggests, it comes from a point, and illuminates in all directions.

// create the light - the first parameter is the color, the second, the intensity
var light=new THREE.PointLight(0xFFFFFF,2);

// set the position (x,y,z) - in this case, straight from behind and above the camera.
light.position.set(0,3,3);

// add the light to the scene
scene.add(light);

For the material, we’ll simply switch our MeshBasicMaterial for a MeshPhongMaterial. We could customize it further – it has parameters that MeshBasicMaterial doesn’t – but we can get by for now just setting its color:

var material = new THREE.MeshPhongMaterial({color: 0x2266ff})

See the Pen KzmJXE by Lucas Bebber (@lbebber) on CodePen.

Now we’re getting somewhere! If you want you can take a look at the documentation and play with different lights, materials and parameters.

Animate

To animate, we have to tell the renderer to render every time we want to update something. The best way to do this in browsers, guaranteeing the best performance, is to use requestAnimationFrame, which I covered in the Introduction to Canvas article. To recap, this is how it works:

function update(){
  // do your rendering operations
  ...
  // tells the browser to call this function again next frame
  requestAnimationFrame(update);
}
update();

So here we’ll rotate the box a little every frame and then render. Note that it’s usually not the best practice to update the scene in the same loop as the rendering loop – this ties the animation to the rendering, so the animation will run at a slower speed in slower devices, is subject to hiccups, etc. However, we’ll do that here for now, for the sake of simplicity:

function update(){
  // rotates the box a bit
  box.rotation.y+=0.02;
  // render the scene
  renderer.render(scene, camera);

  requestAnimationFrame(update);
}
update();

And here’s our spinning, shaded box in all its glory:

See the Pen QNvYBP by Lucas Bebber (@lbebber) on CodePen.

From here you can take a look at the three.js documentation and experiment with different objects, textures, animations, and so on. We’ll cover more advanced techniques in the next articles. Cheers!

Code