Three.js

WebGL the easy way

Chandler Prall
Jan 21st, 2013

@chandlerprall
http://www.chandlerprall.com

What is Three.js

http://mrdoob.github.com/three.js/

Scene Graph



new THREE.PerspectiveCamera( fov, aspect_ratio, near, far )
new THREE.OrthographicCamera( left, right, top, bottom, near, far )
new THREE.Mesh( geometry, material )

Geometry


Geometry

new THREE.CubeGeometry(
  width, height, depth,
  width_segments, height_segments, depth_segments
)
new THREE.SphereGeometry( radius, segments_width, segments_height )
new THREE.PlaneGeometry(
  width, height,
  width_segments, height_segments
)

Geometry

var loader = new THREE.JSONLoader();

loader.load( 'octopus.js', function ( geometry, materials ) {
  var octopus = new THREE.Mesh(
    geometry,
    new THREE.MeshFaceMaterial( materials )
  );
  scene.add( octopus );
});

Material



Material

new THREE.MeshLambertMaterial({ color: 0xFF0000, wireframe: true })
new THREE.MeshLambertMaterial({ color: 0xFF0000,
                                shading: THREE.FlatShading })
new THREE.MeshLambertMaterial({ color: 0xFF0000 })
new THREE.MeshLambertMaterial({
                  map: THREE.ImageUtils.loadTexture( 'sand.jpg' ) })

HTML5 Template

<!DOCTYPE html>
<html>
    <head>
        <script src="js/three.min.js"></script>
        <script>
        window.onload = function() {

        };
        </script>
    </head>

    <body></body>
</html>

Basic Scene

window.onload = function() {
    // Create renderer and add it to the page
    var renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    // Create a scene to hold our awesome 3D world
    var scene = new THREE.Scene;

    // Create a camera
    var camera = new THREE.PerspectiveCamera(
        45,                                     // FOV
        window.innerWidth / window.innerHeight, // Aspect Ratio
        1,                                      // Near plane
        1000                                    // Far plane
    );
    camera.position.set( 30, 30, 30 ); // Position camera
    camera.lookAt( scene.position ); // Look at the scene origin
};

Add Objects

var ground = new THREE.Mesh(
    new THREE.PlaneGeometry( 30, 30 ),
    new THREE.MeshLambertMaterial({ color: 0x33CC33 })
);
// PlaneGeometry is created along YZ axis, rotate -90 degrees
// around the X axis so it lays flat and pointing up 
ground.rotation.x = -Math.PI / 4;
scene.add( ground );

var box = new THREE.Mesh(
    new THREE.CubeGeometry( 10, 10, 10 ),
    new THREE.MeshLambertMaterial({ color: 0xDD3344 })
);
box.position.y = 5;
scene.add( box );

Render!

var render = function() {
    // use requestAnimationFrame to create a render loop
    requestAnimationFrame( render );
    renderer.render( scene, camera );
};
render();

Lighting

var ambient_light = new THREE.AmbientLight( 0x333333 );
scene.add( ambient_light );

var sun_light = new THREE.DirectionalLight( 0xBBBBBB );
sun_light.position.set( 1, .5, 0 );
scene.add( sun_light );

Shadows

renderer.shadowMapEnabled = true;
ground.receiveShadow = true;
box.castShadow = true;

sun_light.castShadow = true;

sun_light.shadowCameraNear = 1;
sun_light.shadowCameraFar = 100;

sun_light.shadowCameraLeft = -50;
sun_light.shadowCameraRight = 50;
sun_light.shadowCameraTop = -50;
sun_light.shadowCameraBottom = 50;

sun_light.shadowBias = -.01;

~70 lines of code



Interactive Editor


http://mrdoob.com/projects/htmleditor

Animation


function animate() {
  requestAnimationFrame( animate );
  
  mesh.position.y += 0.01;

  renderer.render( scene, camera );
}

Animation - Morph Targets




Animation - Morph Targets


loader.load( 'spy/spy.js', function ( geometry, materials ) {
  for ( var i = 0; i < materials.length; i++ ) {
    // Tell all materials in spy.js to use morph targets
    materials[ i ].morphTargets = true;
  }

  var spy = new THREE.Mesh(
    geometry,
    new THREE.MeshFaceMaterial( materials )
  );

  scene.add( spy );
});

Animation - Morph Targets

var duration = 1000, keyframes = 4, currentKeyFrame = 0,
    interpolation = duration / keyframes, lastKeyframe = 0;
var render = function() {
  requestAnimationFrame( render );
  var time = Date.now() % duration,
      keyframe = Math.floor( time / interpolation );
  if ( keyframe != currentKeyframe ) {
    mesh.morphTargetInfluences[ lastKeyframe ] = 0;
    mesh.morphTargetInfluences[ currentKeyframe ] = 1;
    mesh.morphTargetInfluences[ keyframe ] = 0;
    lastKeyframe = currentKeyframe;
    currentKeyframe = keyframe;
  }
  mesh.morphTargetInfluences[ keyframe ] = ( time % interpolation )
                                                / interpolation;
  mesh.morphTargetInfluences[ lastKeyframe ] = 1 - 
                              mesh.morphTargetInfluences[ keyframe ];
  renderer.render( scene, camera );
}

Physics

Physijs

Physics + Three.js

<script type="text/javascript" src="physi.js"></script>
<script type="text/javascript">
  Physijs.scripts.worker = '/js/physijs_worker.js';
  Physijs.scripts.ammo = '/js/ammo.js';

  var scene = new Physijs.Scene;

  var mesh = new Physijs.BoxMesh(
    new THREE.CubeGeometry( 5, 5, 5 ),
    new THREE.MeshLambertMaterial({ color: 0xFF0000 })
  );

  var render = function() {
    requestAnimationFrame( render );
    scene.simulate(); // run physics
    renderer.render( scene, camera ); // render the scene
  };
</script>

Physijs


Primitive Shapes
BoxMesh, SphereMesh, CylinderMesh, CapsuleMesh, ConeMesh


Complex Shapes
ConvexMesh, ConcaveMesh, HeightfieldMesh


Constraints
Ball and Socket, Hinges, Sliders, etc


Vehicles

Game Time!






Interactive editor, game edition
http://www.chandlerprall.com/games/spygame

Resources

Thank you

Model & Texture Credits


Camera Icon, Thomas Le Bas, from The Noun Project
Other Icons, Lorc
Octopus model, Arvid Rudling
Spy model, Wopr2012
Rubix Cube model, Coolinus

One or more textures have been created with images from CGTextures.com. These images may not be redistributed by default, please visit www.cgtextures.com for more information