Beginning with 3D WebGL (pt. 4) - Animation
This is part 4 in a series for beginners wanting to get in to using 3D WebGL. If you haven't checked out the other post be sure to have a look:
- pt 1. The Scene
- pt 2. Geometry
- pt 3. Materials
- pt 4. Animation
So its been a little while since part 3, and in the time since I was trying to decide whether I wanted to bring animation in to the series at this point, when we still have so much more 3D we can cover. In the end I thought, bugger it, lets have some fun!
In the previous posts we've learned how to create and control a number of things:
- a mesh's position, rotation, and material
- mesh vertices position
- camera position & rotation
- lighting position and direction
Given we have direct control each of these properties, we can also animate them. This is actually part of the magic of 3D work for me, there are so many variables to animate in a scene, the potential combinations for animated effects are endless.
Animating a Three.js scene: the render loop.
The key to animating your 3D scene is to create a render loop with a requestAnimationFrame
function, and rendering your scene within the loop. This means any change you make to the scene will be immediately rendered, allowing you to animate by changing properties with tweens or your own JavaScript.
// a render loop
function render() {
requestAnimationFrame(render);
// render the scene
renderer.render(scene, camera);
};
Incorporating a tweening library in to your 3D Scene
A fun way to animate the properties in our 3D scene is to tween them. If you're not familiar with what a JavaScript tween is, I've written about them before here.
An Example: Tweening an icosahedron's vertices
Using our knowledge from previous posts I've created a scene with an icosahedron.
Now we have a mesh with a list of vertices. We're going to tween the position of each of these vertices a random distance from their original position. So first we'll store the position of the original icosahedron shape:
function getOriginalVerticePositions() {
// go through each vertice geometry and store their position in an array
for (var i = 0, l = geometry.vertices.length; i<l; i++) {
verticePositions.push({x: geometry.vertices[i].x, y: geometry.vertices[i].y});
}
}
Now we have the positions of the vertices, we can create a tween for each of the vertices, which will animate them to their new position. I'm using GSAP TweenLite here but you could use any Tweening engine or library to do this.
function getNewVertices() {
/* this function returns an array of vertice positions which are randomised
from the original vertice position */
var newVertices = [];
for (var i = 0, l = geometry.vertices.length; i<l; i++) {
newVertices[i] = {
x: verticePositions[i].x -5 + Math.random()*10,
y: verticePositions[i].y -5 + Math.random()*10
}
}
return newVertices;
}
function tweenIcosahedron() {
var newVerticePositions = getNewVertices();
// tween each vertice to their new position
for (var i = 0; i < geometry.vertices.length; i++) {
tweenVertice(i, newVerticePositions);
}
}
function tweenVertice(i, newVerticePositions) {
// set the tween
TweenLite.to(geometry.vertices[i], 1, {x: newVerticePositions[i].x, y: newVerticePositions[i].y, ease: Back.easeInOut, onComplete: function() {
// start the icosahedron tween again now the animation is complete
if (i === 0) tweenIcosahedron();
}});
}
This creates an animated morphing icosahedron!
That looks pretty cool (if I say so myself) but why don't we try tweening another element of the scene: the rotation of the mesh.
// the new tween function
function tweenIcosohedron() {
// create a random rotation aount
var rotation = {x: Math.random()*3, y: Math.random()*3, z: Math.random()*3};
// tween the mesh's rotation property to the new position
TweenLite.to(mesh.rotation, 1, {x: rotation.x, y: rotation.y, z: rotation.z,
ease: Back.easeInOut, onComplete: tweenIcosohedron});
var newVerticePositions = getNewVertices();
for (var i = 0; i < geometry.vertices.length; i++) {
tweenVertice(i, newVerticePositions);
}
}
This creates an even more interesting animation!
Given you can tween any combination of properties in a 3D scene, what tween animations would you create?
Another cool effect: animating the scene's camera
The animation possibilities in a 3D scene extend to animation of the camera that films that scene. By animating your camera you can really explore the 3D space you've set up.
Here is my somewhat substandard attempt at creating a kind of "asteroid field" in space (3D art has never really been my strong point).
This scene looks very static right now. You know what would make it better? If we jumped in a space ship and flew through the field, seeing the rocks fly past us. I don't have a space ship but I do have control over my scene camera, maybe I can animate it to seem like we're flying through the rocks.
function updateCamPosition() {
// rotate our camera's position on the z/y axis
angle += 0.005;
var z = 100 * Math.cos(angle);
var y = 100 * Math.sin(angle);
camera.position.z = z;
camera.position.y = y;
/* rotate the camera so the angle it faces animates -
there's no exact science to this - I just picked a
random percentage of the z position */
camera.rotation.x = z*0.02;
}
To make our scene even more dynamic, I'm going to rotate each of my space rock meshes.
Rock.prototype.rotate = function() {
this.mesh.rotation.x += this.vr.x;
this.mesh.rotation.y += this.vr.y;
}
And we're flying!!
These are just two examples - the tip of the iceberg
In future posts we'll look at how to animate particles, as well as incorporate user input in to animating our 3D scenes. But I hope these two examples have inspired you to think about what you'd like to animate in your 3D scenes. Make sure you share with us your own 3D animations in the comments below!
Be sure to check back for future posts, where we'll look at user input, particles, shaders, and more.
Your questions and suggestions give me ideas for what to write about next! So if you have any questions please comment below, send me a tweet @rachsmithtweets, submit to my AMA, or flick me an email at contact at rachsmith dot com.