WELCOME TO RATIO CLUB

Week 6

3D

Three.js

Three.js is a javascript library designed to build interactive 3D scenes directly inside the browser. Everything is rendered on the client side. It allows us to make really complicated, fully interactive 3D animations that load on the audiences' computer almost instantly.

Like P5, everything is drawn inside a canvas element that Three creates for you.

Also like P5 we do all our coding in two functions:

init()
animate()

However, unlike P5 we must remember to call both of these ourselves to run our animation.

init();
animate();

function init(){
   // Our setup code goes here
}

function animate(){
  requestAnimationFrame(animate);
   // Our animation code goes here.
  renderer.render(scene, camera);
}

The mechanics of Three.js has a distinct structure that we have to build up with our code. At first it may seem complicated but it makes a lot of sense and is the way most 3D graphics are made.

Camera, Scene & Renderer

To draw anything in Three.js you need to use 3 types of object: a camera, a scene and a renderer.

The renderer translates all your beautiful code into what you see,
using WebGL (Web Graphics Language)

You set the size of the canvas element using renderer.setSize( width, height );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

The camera object determines what will be shown on screen.

var camera = new THREE.PerspectiveCamera(
  75,             // FOV (Field of View in deg)
  width / height, // Aspect-Ratio
  0.1,            // Near Frustum (close limit)
  1000            // Far Frustum (far limit)
 );

By default, the camera is placed in the center of our 3D world (0,0,0) so we must move back along the Z axis:

  camera.position.z = 5;

The scene is the object where the fun happens. This is the object to which we add all the shapes we want to see.

var scene = new THREE.Scene();

Finally, we must use the Renderer to render the scene, using the camera.

renderer.render( scene, camera );

Mesh: Geometry & Material

In 3D computer graphics, all shapes, no matter how intricate, can be made of a mesh of triangles.

Triangles are the fabric of the universe.

In Three.js we use a Mesh Object for every shape we want to draw. Each Mesh is made of two parts: a Geometry and a Material

The Geometry holds all the vectors and all the geometric arrangement of our shape.

We can build our own shape by pushing vectors to our Geometry. These vectors are special arrays that contain the vertices of our shape. The space between each vertex of our shape are the faces of our shape:

var geometry = new THREE.Geometry();

geometry.vertices.push( new THREE.Vector3( 10, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 0, 10, 0 ) );
geometry.vertices.push( new THREE.Vector3( 0, 0, 10 ) );
geometry.vertices.push( new THREE.Vector3( 10, 0, 0 ) );

There are also a whole host of premade Geometry objects that will do the maths of drawing a Cube, Sphere, Cylinder or Torus (Donut/Mug)

If the Geometry is the skeleton of our shape, the Material object is the skin we wrap around it.

var material = new THREE.MeshBasicMaterial();

material.color = new THREE.Color( 0x00FF00 );
material.color = new THREE.Color( "#00FF00" );
material.color = new THREE.Color( "rgb(255, 0, 125)" );
material.color = new THREE.Color( "hsl(5, 100%, 50%)" );
material.color = new THREE.Color( 1.0, 0.8, 0.0 );

Materials come in a number of types each with different properties.

THREE.MeshBasicMaterial(); // uniform color/shading
THREE.MeshLambertMaterial(); // Matt finish
THREE.MeshPhongMaterial(); // Glossy finish
THREE.MeshNormalMaterial(); // multicolour (RGB)

Each of these control how light bounces off the surface of our shapes. For Lambert and Phong materials to be visible, you must add your own light.

The Geometry and Material are added to the Mesh Object, and the Mesh is added to the Scene.

var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

Textures and UV coordinates

The Material Object also controls how colours blend on different parts of our shape, and how textures are mapped and stetched over the surface of our shape.

Textures are patterns or images that are imported and mapped around the shapes we want to draw.

var texture = new THREE.TextureLoader();

Textures must be loaded using the load function of the texture object.

texture.load( "textures/water.jpg" );

The texture is added to the Material object using the key term map, and by default the texture will be mapped onto every triangle in the Geometry.

material.map = texture;

However, this can be changed by setting the wrapS and wrapT attributes of the texture object.

tex.wrapS = THREE.ClampToEdgeWrapping;
tex.wrapT = THREE.ClampToEdgeWrapping;

What is happening here is UV mapping.

U is the horizontal coordinate using for texture mapping, and V is the vertical coordinate.

"The UV mapping process involves assigning pixels in the image to surface mappings on the polygon, usually done by "programmatically" copying a triangular piece of the image map and pasting it onto a triangle on the object." Wikipedia

Essentially what's happening here is the UV coordinates are being matched with the vertices of the Geometry. Luckily, Three.js will often be able to calculate the UV mapping for us, based on proportions.

Transformations

We can move all our shapes around by performing transformations on the Mesh.

Translation

mesh.position.x = 100; // Move 100 pixels right

mesh.position.z = -250; // Move 250 pixels away

mesh.position.y += 25; // Move up 25 pixels every frame

mesh.position.set( 10, 20, 5 ); // Move on all axes at the same time.

Scale

mesh.scale.set( 2, 2, 2 ); // Double in size uniformly

mesh.scale.set( 0.75, 0.75, 0.75 ); // Reduce size by a quarter

mesh.scale.set( 0.5, 6.0, 1.0 );
// Make half as wide along the X axis, six times taller on the Y axis, an keep the Z axis depth the same

Rotation

mesh.rotation.x = Math.PI * 4 / 3;
// Rotate by four thirds PI Radians (120°) around the X axis

mesh.rotation.y = 0.5;
// Rotate by Half PI Radians (90°) around the Y axis

mesh.rotation.z = THREE.Math.degToRad(45);
// Rotate by Quarter PI Radians (45°) around the Z axis

Rotation in Three.js is measured in Radians which are all fractions of Pi.

The function THREE.Math.degToRad() will convert Degrees to Radians.

( Radians in red )

Recap

So to recap all that:

var camera, scene, renderer, mesh;

init();
animate();

function init(){
  scene = new THREE.Scene();

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

  renderer = new THREE.WebGLRenderer();
  document.body.appendChild( renderer.domElement );

  var geometry = new THREE.SphereGeometry( 5, 32, 32 );

  var texture = new THREE.TextureLoader().load("img.jpg");

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

  mesh = new THREE.Mesh( geometry, material );

  scene.add( mesh );

  renderer.render( scene, camera );
}

function animate(){
  requestAnimationFrame(animate);

  mesh.rotation.y += Math.PI * 0.1;

  renderer.render(scene, camera);
}

PHEEEEEEW!

Much credit due to David Scott Lyons

EXERCISE

Let's get making some 3D fun.

Three.js Documentation

I've put together some bootstrap code. Read the Three.js docs and have a go at drawing some geometries and materials, adding them to meshes and then showing them in your scene!

Homework

This week I want you to plan the steps you think you need to take to make your project a reality.

Next week I'll help everyone with their step-by-step plan to fill in what some of the steps might be, and to figure out what libraries/modules/technologies you may need to look at.