//Three.js Tutorial 'The Aviator'
//https://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
#world
View Compiled
#world
position: absolute
width: 100vw
height: 100vh
overflow: hidden
background: linear-gradient(moccasin, palegoldenrod)
View Compiled
var Colors = {
red: 0xf25346,
white: 0xd8d0d1,
brown: 0x59332e,
pink: 0xF5986E,
brownDark: 0x23190f,
blue: 0x609dc8
};
var matRed = new THREE.MeshPhongMaterial({
color: Colors.red,
shading: THREE.FlatShading
});
var matWhite = new THREE.MeshPhongMaterial({
color: Colors.white,
shading: THREE.FlatShading
});
var matBrown = new THREE.MeshPhongMaterial({
color: Colors.brown,
shading: THREE.FlatShading
});
var matBrownDark = new THREE.MeshPhongMaterial({
color: Colors.brownDark,
shading: THREE.FlatShading
});
var mousePos = {x:0, y:0};
var sky, sea, airplane;
var hemisphereLight, shadowLight, ambientLight;
var scene, camera, fieldOfView, aspectRatio, nearPlane, farPlane, HEIGHT, WIDTH, renderer, container;
var Pilot = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = 'Pilot';
this.angleHairs = 0;
var bodyGeom = new THREE.BoxGeometry(15,15,15);
var body = new THREE.Mesh(bodyGeom, matBrown);
body.position.set(2,-12,0);
this.mesh.add(body);
var faceGeom = new THREE.BoxGeometry(10,10,10);
var faceMat = new THREE.MeshLambertMaterial({
color: Colors.pink
});
var face = new THREE.Mesh(faceGeom, faceMat);
this.mesh.add(face);
var hairGeom = new THREE.BoxGeometry(4,4,4);
var hairMat = new THREE.MeshLambertMaterial({
color: Colors.brown
});
var hair = new THREE.Mesh(hairGeom, hairMat);
hair.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0,2,0));
var hairs = new THREE.Object3D();
this.hairsTop = new THREE.Object3D();
for (var i = 0; i < 12; i++){
var h = hair.clone();
var col = i % 3;
var row = Math.floor(i/3);
var startPosZ = -4;
var startPosX = -4;
h.position.set(startPosX + row * 4, 0, startPosZ + col * 4);
this.hairsTop.add(h);
}
hairs.add(this.hairsTop);
var hairSideGeom = new THREE.BoxGeometry(12,4,2);
hairSideGeom.applyMatrix(new THREE.Matrix4().makeTranslation(-6,0,0));
var hairSideR = new THREE.Mesh(hairSideGeom, hairMat);
var hairSideL = hairSideR.clone();
hairSideR.position.set(8,-2,6);
hairSideL.position.set(8,-2,-6);
hairs.add(hairSideR);
hairs.add(hairSideL);
var hairBackGeom = new THREE.BoxGeometry(2,8,10);
var hairBack = new THREE.Mesh(hairBackGeom, hairMat);
hairBack.position.set(-1,-4,0);
hairs.add(hairBack);
hairs.position.set(-5,5,0);
this.mesh.add(hairs);
var glassGeom = new THREE.BoxGeometry(5,5,5);
var glassMat = new THREE.MeshLambertMaterial({
color: Colors.brown
});
var glassR = new THREE.Mesh(glassGeom, glassMat);
glassR.position.set(6,0,3);
var glassL = glassR.clone();
glassL.position.set(6,0,-3);
var glassAGeom = new THREE.BoxGeometry(11,1,11);
var glassA = new THREE.Mesh(glassGeom, glassMat);
this.mesh.add(glassR);
this.mesh.add(glassL);
this.mesh.add(glassA);
var earGeom = new THREE.BoxGeometry(2,3,2);
var earR = new THREE.Mesh(earGeom, faceMat);
earR.position.set(0,0,6);
var earL = earR.clone();
earL.position.set(0,0,-6);
this.mesh.add(earR);
this.mesh.add(earL);
};
Pilot.prototype.updateHairs = function(){
var hairs = this.hairsTop.children;
var l = hairs.length;
for(var i = 0; i < l; i++){
var h = hairs[i];
h.scale.y = 0.75 + Math.cos(this.angleHairs + i / 3) * 0.25;
}
this.angleHairs += 0.16;
}
var AirPlane = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = 'airPlane';
//cockpit
var geomCockpit = new THREE.BoxGeometry(80,50,50,1,1,1);
geomCockpit.vertices[4].y -= 10;
geomCockpit.vertices[4].z += 20;
geomCockpit.vertices[5].y -= 10;
geomCockpit.vertices[5].z -= 20;
geomCockpit.vertices[6].y += 30;
geomCockpit.vertices[6].z += 20;
geomCockpit.vertices[7].y += 30;
geomCockpit.vertices[7].z -= 20;
var cockpit = new THREE.Mesh(geomCockpit, matRed);
cockpit.castShadow = true;
cockpit.receiveShadow = true;
this.mesh.add(cockpit);
//engine
var geomEngine = new THREE.BoxGeometry(20,50,50,1,1,1);
var engine = new THREE.Mesh(geomEngine, matWhite);
engine.position.x = 50;
engine.castShadow = true;
engine.receiveShadow = true;
this.mesh.add(engine);
//tail
var geomTailPlane = new THREE.BoxGeometry(15,20,5,1,1,1);
var tailPlane = new THREE.Mesh(geomTailPlane, matRed);
tailPlane.position.set(-40,25,0);
tailPlane.castShadow = true;
tailPlane.receiveShadow = true;
this.mesh.add(tailPlane);
//wings
var geomSideWing = new THREE.BoxGeometry(30,5,120,1,1,1);
var sideWing = new THREE.Mesh(geomSideWing, matRed);
sideWing.position.set(0,15,0);
sideWing.castShadow = true;
sideWing.receiveShadow = true;
this.mesh.add(sideWing);
//windshield
var geomWindShield = new THREE.BoxGeometry(3,15,20,1,1,1);
var matWindShield = new THREE.MeshPhongMaterial({
color: Colors.white,
transparent: true,
opacity: 0.3,
shading: THREE.FlatShading
});
var windshield = new THREE.Mesh(geomWindShield, matWhite);
windshield.position.set(5,27,0);
windshield.castShadow = true;
windshield.receiveShadow = true;
this.mesh.add(windshield);
//propeller
var geomPropeller = new THREE.BoxGeometry(20,10,10,1,1,1);
geomPropeller.vertices[4].y -= 5;
geomPropeller.vertices[4].z += 5;
geomPropeller.vertices[5].y -= 5;
geomPropeller.vertices[5].z -= 5;
geomPropeller.vertices[6].y += 5;
geomPropeller.vertices[6].z += 5;
geomPropeller.vertices[7].y += 5;
geomPropeller.vertices[7].z -= 5;
this.propeller = new THREE.Mesh(geomPropeller, matBrown);
this.propeller.castShadow = true;
this.propeller.receiveShadow = true;
//blades
var geomBlade = new THREE.BoxGeometry(1,80,10,1,1,1);
var blade1 = new THREE.Mesh(geomBlade, matBrownDark);
blade1.position.set(8,0,0);
blade1.castShadow = true;
blade1.receiveShadow = true;
var blade2 = blade1.clone();
blade2.rotation.x = Math.PI / 2;
blade2.castShadow = true;
blade2.receiveShadow = true;
this.propeller.add(blade1);
this.propeller.add(blade2);
this.propeller.position.set(60,0,0);
this.mesh.add(this.propeller);
//wheels
var wheelProtecGeom = new THREE.BoxGeometry(30,15,10,1,1,1);
var wheelProtecR = new THREE.Mesh(wheelProtecGeom, matRed);
wheelProtecR.position.set(25,-20,25);
this.mesh.add(wheelProtecR);
var wheelProtecL = wheelProtecR.clone();
wheelProtecL.position.z = -wheelProtecR.position.z;
this.mesh.add(wheelProtecL);
var wheelGeom = new THREE.BoxGeometry(24,24,4);
var wheelR = new THREE.Mesh(wheelGeom, matBrownDark);
wheelR.position.set(25,-28,25);
var wheelAxisGeom = new THREE.BoxGeometry(10,10,6);
var wheelAxis = new THREE.Mesh(wheelAxisGeom, matBrown);
wheelR.add(wheelAxis);
this.mesh.add(wheelR);
var wheelL = wheelR.clone();
wheelL.position.z = -wheelR.position.z;
this.mesh.add(wheelL);
var wheelB = wheelR.clone();
wheelB.scale.set(0.5,0.5,0.5);
wheelB.position.set(-35,-5,0);
this.mesh.add(wheelB);
var suspensionGeom = new THREE.BoxGeometry(4,20,4);
suspensionGeom.applyMatrix(new THREE.Matrix4().makeTranslation(0,10,0));
var suspension = new THREE.Mesh(suspensionGeom, matRed);
suspension.position.set(-35,-5,0);
suspension.rotation.z = -0.3;
this.mesh.add(suspension);
this.pilot = new Pilot();
this.pilot.mesh.position.set(-10,27,0);
this.mesh.add(this.pilot.mesh);
this.mesh.castShadow = true;
this.mesh.receiveShadow = true;
};
var Cloud = function(){
this.mesh = new THREE.Object3D();
var geom = new THREE.BoxGeometry(20,20,20);
var mat = new THREE.MeshPhongMaterial({
color: Colors.white
});
var nBlocs = 3 + Math.floor(Math.random() * 3);
for (var i = 0; i < nBlocs; i++){
var m = new THREE.Mesh(geom, mat);
var s = 0.1 + Math.random() * 0.9;
m.position.x = i * 15;
m.position.y = Math.random() * 10;
m.position.z = -5 + Math.random() * 5;
m.rotation.z = Math.random() * Math.PI * 2;
m.rotation.y = Math.random() * Math.PI * 2;
m.scale.set(s,s,s);
m.castShadow = true;
m.receiveShadow = true;
this.mesh.add(m);
}
};
var Sky = function(){
this.mesh = new THREE.Object3D();
this.nClouds = 20;
var stepAngle = Math.PI * 2 / this.nClouds;
for(var i = 0; i < this.nClouds; i++){
var c = new Cloud();
var a = stepAngle * i;
var h = 750 + Math.random() * 200;
var s = 1 + Math.random() * 2;
c.mesh.position.y = Math.sin(a) * h;
c.mesh.position.x = Math.cos(a) * h;
c.mesh.position.z = -400 - Math.random() * 300;
c.mesh.rotation.z = a + Math.PI / 2;
c.mesh.scale.set(s,s,s);
this.mesh.add(c.mesh);
}
};
var Sea = function(){
var geom = new THREE.SphereGeometry(700,50,50);
geom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI/2));
geom.mergeVertices();
var l = geom.vertices.length;
this.waves = [];
for (var i = 0; i < l; i++){
var v = geom.vertices[i];
this.waves.push({
x: v.x,
y: v.y,
z: v.z,
ang: Math.random() * Math.PI * 2,
amp: 5 + Math.random() * 15,
speed: 0.016 + Math.random() * 0.032
});
}
var mat = new THREE.MeshLambertMaterial({
color: Colors.blue,
shading: THREE.FlatShading,
transparent: true,
opacity: 0.8
});
this.mesh = new THREE.Mesh(geom, mat);
this.mesh.receiveShadow = true;
};
Sea.prototype.moveWaves = function(){
var verts = this.mesh.geometry.vertices;
var l = verts.length;
for(var i = 0; i < l; i++){
var v = verts[i];
var vprops = this.waves[i];
v.x = vprops.x + Math.cos(vprops.ang) * vprops.amp;
v.y = vprops.y + Math.sin(vprops.ang) * vprops.amp;
vprops.ang += vprops.speed;
}
this.mesh.geometry.verticesNeedUpdate = true;
sea.mesh.rotation.z += 0.005;
}
function normalize(v, vmin, vmax, tmin, tmax){
var nv = Math.max(Math.min(v, vmax), vmin);
var dv = vmax - vmin;
var pc = (nv - vmin) / dv;
var dt = tmax - tmin;
var tv = tmin + (pc * dt);
return tv;
}
function createPlane(){
airplane = new AirPlane();
airplane.mesh.scale.set(0.25,0.25,0.25);
airplane.mesh.position.y = -100;
scene.add(airplane.mesh);
}
function updatePlane(){
var targetX = normalize(mousePos.x, -1, 1, -100, 100);
var targetY = normalize(mousePos.y, -1, 1, 25, 175);
airplane.mesh.position.y += (targetY - airplane.mesh.position.y) * 0.1;
airplane.mesh.rotation.x = (airplane.mesh.position.y - targetY) * 0.0064;
airplane.mesh.rotation.z = (targetY - airplane.mesh.position.y) * 0.0128;
airplane.propeller.rotation.x += 0.3;
}
function createSky(){
sky = new Sky();
sky.mesh.position.y = -600;
sky.mesh.position.z = 400;
scene.add(sky.mesh);
}
function createSea(){
sea = new Sea();
sea.mesh.position.y = -700;
sea.mesh.position.z = -300;
scene.add(sea.mesh);
}
function createLights() {
hemisphereLight = new THREE.HemisphereLight(0xaaaaaa, 0x000000, 0.9);
ambientLight = new THREE.AmbientLight(0xdc8874, 0.5);
shadowLight = new THREE.DirectionalLight(0xffffff, 0.9);
shadowLight.position.set(150,350,350);
shadowLight.castShadow = true;
shadowLight.shadow.camera.left = -400;
shadowLight.shadow.camera.right = 400;
shadowLight.shadow.camera.top = 400;
shadowLight.shadow.camera.bottom = -400;
shadowLight.shadow.camera.near = 1;
shadowLight.shadow.camera.far = 1000;
shadowLight.shadow.mapSize.width = 2048;
shadowLight.shadow.mapSize.height = 2048;
scene.add(hemisphereLight);
scene.add(ambientLight);
scene.add(shadowLight);
}
function handleWindowResize() {
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH/HEIGHT;
camera.updateProjectionMatrix();
}
function handleMouseMove(e){
var tx = -1 + (e.clientX / WIDTH) * 2;
var ty = 1 - (e.clientY / HEIGHT) * 2;
mousePos = {x:tx, y:ty};
}
function createScene(){
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xc9c9b0, 100, 950);
aspectRatio = WIDTH/HEIGHT;
fieldOfView = 65;
nearPlane = 0.1;
farPlane = 10000;
camera = new THREE.PerspectiveCamera(
fieldOfView,
aspectRatio,
nearPlane,
farPlane
);
camera.position.x = 0;
camera.position.y = 100;
camera.position.z = 170;
renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
renderer.setSize(WIDTH, HEIGHT);
renderer.shadowMap.enabled = true;
container = document.getElementById('world');
container.appendChild(renderer.domElement);
window.addEventListener('resize', handleWindowResize, false);
}
function loop(){
airplane.pilot.updateHairs();
sea.moveWaves();
sky.mesh.rotation.z += 0.005;
updatePlane();
renderer.render(scene, camera);
requestAnimationFrame(loop);
}
function init(){
createScene();
createLights();
createPlane();
createSea();
createSky();
document.addEventListener('mousemove', handleMouseMove, false);
loop();
}
window.addEventListener('load', init, false);
This Pen doesn't use any external CSS resources.