A Three.js rocket ship made from basic geometric shapes.
A Pen by Andrew Khassapov on CodePen.
<!--Recreated stivaliserna's rocket cartoon with Three.js geometric shapes. See: https://codepen.io/stivaliserna/pen/rNMwpaG--> | |
<div class="rain rain1"></div> | |
<div class="rain rain2"> | |
<div class="drop drop2"></div> | |
</div> | |
<div class="rain rain3"></div> | |
<div class="rain rain4"></div> | |
<div class="rain rain5"> | |
<div class="drop drop5"></div> | |
</div> | |
<div class="rain rain6"></div> | |
<div class="rain rain7"></div> | |
<div class="rain rain8"> | |
<div class="drop drop8"></div> | |
</div> | |
<div class="rain rain9"></div> | |
<div class="rain rain10"></div> | |
<div class="drop drop11"></div> | |
<div class="drop drop12"></div> | |
<div id="canvas"></div> <!-- Main canvas //--> |
let scene, | |
camera, | |
fieldOfView, | |
aspectRatio, | |
nearPlane, | |
farPlane, | |
renderer, | |
container, | |
rocket, | |
rocket_fire, | |
HEIGHT, | |
WIDTH; | |
const createScene = () => { | |
HEIGHT = window.innerHeight; | |
WIDTH = window.innerWidth; | |
scene = new THREE.Scene(); | |
//scene.fog = new THREE.Fog(0x03032b, 10, 1500); | |
aspectRatio = WIDTH / HEIGHT; | |
fieldOfView = 60; | |
nearPlane = 1; | |
farPlane = 10000; | |
camera = new THREE.PerspectiveCamera( | |
fieldOfView, | |
aspectRatio, | |
nearPlane, | |
farPlane | |
); | |
camera.position.x = 0; | |
camera.position.z = 200; | |
camera.position.y = 0; | |
renderer = new THREE.WebGLRenderer({ | |
alpha: true, | |
antialias: true | |
}); | |
renderer.setSize(WIDTH, HEIGHT); | |
renderer.shadowMap.enabled = true; | |
container = document.getElementById("canvas"); | |
container.appendChild(renderer.domElement); | |
window.addEventListener("resize", handleWindowResize, false); | |
// Create rocket group | |
const rocket_topc = new THREE.Mesh( | |
new THREE.CylinderGeometry(0, 6, 4, 64), | |
new THREE.MeshStandardMaterial({ color: 0xff0000 }) | |
); | |
scene.add(rocket_topc); | |
rocket_topc.position.y = 60; | |
const rocket_topa = new THREE.Mesh( | |
new THREE.CylinderGeometry(6, 12, 8, 64), | |
new THREE.MeshStandardMaterial({ color: 0xff0000 }) | |
); | |
scene.add(rocket_topa); | |
rocket_topa.position.y = 54; | |
const rocket_topb = new THREE.Mesh( | |
new THREE.CylinderGeometry(12, 18, 20, 64), | |
new THREE.MeshStandardMaterial({ color: 0xff0000 }) | |
); | |
scene.add(rocket_topb); | |
rocket_topb.position.y = 40; | |
const rocket_mida = new THREE.Mesh( | |
new THREE.CylinderGeometry(18, 20, 20, 64), | |
new THREE.MeshStandardMaterial({ color: 0xffffff }) | |
); | |
scene.add(rocket_mida); | |
rocket_mida.position.y = 20; | |
const rocket_midb = new THREE.Mesh( | |
new THREE.CylinderGeometry(20, 18, 20, 64), | |
new THREE.MeshStandardMaterial( | |
{ color: 0xffffff }, | |
(metallness = 1), | |
(roughness = 0) | |
) | |
); | |
scene.add(rocket_midb); | |
const rocket_bota = new THREE.Mesh( | |
new THREE.CylinderGeometry(18, 14, 10, 64), | |
new THREE.MeshStandardMaterial({ color: 0xffffff }) | |
); | |
scene.add(rocket_bota); | |
rocket_bota.position.y = -15; | |
const rocket_botb = new THREE.Mesh( | |
new THREE.CylinderGeometry(14, 12, 6, 64), | |
new THREE.MeshStandardMaterial({ | |
color: 0xeeeeee, | |
roughness: 0.5, | |
metalness: 1, | |
side: THREE.DoubleSide | |
}) | |
); | |
scene.add(rocket_botb); | |
rocket_botb.position.y = -20; | |
const rocket_botc = new THREE.Mesh( | |
new THREE.CylinderGeometry(10, 8, 4, 64), | |
new THREE.MeshStandardMaterial({ | |
color: 0x333333, | |
roughness: 0, | |
metalness: 1, | |
side: THREE.DoubleSide | |
}) | |
); | |
scene.add(rocket_botc); | |
rocket_botc.position.y = -22; | |
const rocket_wina = new THREE.Mesh( | |
new THREE.CylinderGeometry(12, 12, 23, 64), | |
new THREE.MeshStandardMaterial({ | |
color: 0xeeeeee, | |
roughness: 0.5, | |
metalness: 1, | |
side: THREE.DoubleSide | |
}) | |
); | |
scene.add(rocket_wina); | |
rocket_wina.position.set(0, 20, 10); | |
rocket_wina.rotation.set(Math.PI / 2, 0, 0); | |
const rocket_winb = new THREE.Mesh( | |
new THREE.CylinderGeometry(9, 9, 24, 64), | |
new THREE.MeshStandardMaterial({ | |
color: 0x0077ff, | |
roughness: 0.6, | |
metalness: 1, | |
side: THREE.DoubleSide | |
}) | |
); | |
scene.add(rocket_winb); | |
rocket_winb.position.set(0, 20, 10); | |
rocket_winb.rotation.set(Math.PI / 2, 0, 0); | |
const rocket_fina = new THREE.Mesh( | |
new THREE.BoxBufferGeometry(40, 8, 18), | |
new THREE.MeshStandardMaterial({ | |
color: 0xff0000 | |
}) | |
); | |
scene.add(rocket_fina); | |
rocket_fina.position.set(16, -10, 0); | |
rocket_fina.rotation.set(Math.PI / 2, 0.7 * Math.PI, 0); | |
const rocket_finb = new THREE.Mesh( | |
new THREE.BoxBufferGeometry(40, 8, 18), | |
new THREE.MeshStandardMaterial({ | |
color: 0xff0000 | |
}) | |
); | |
scene.add(rocket_finb); | |
rocket_finb.position.set(-16, -10, 0); | |
rocket_finb.rotation.set(-Math.PI / 2, 0.7 * Math.PI, 0); | |
var flame_material = new THREE.ShaderMaterial({ | |
uniforms: { | |
color1: { | |
value: new THREE.Color("yellow") | |
}, | |
color2: { | |
value: new THREE.Color("red") | |
} | |
}, | |
vertexShader: ` | |
varying vec2 vUv; | |
void main() { | |
vUv = uv; | |
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); | |
} | |
`, | |
fragmentShader: ` | |
uniform vec3 color1; | |
uniform vec3 color2; | |
varying vec2 vUv; | |
void main() { | |
gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0); | |
} | |
`, | |
wireframe: true | |
}); | |
rocket_fire = new THREE.Mesh( | |
new THREE.CylinderGeometry(6, 0, 20, 64), | |
flame_material | |
); | |
scene.add(rocket_fire); | |
rocket_fire.position.y = -30; | |
rocket = new THREE.Group(); | |
rocket.add( | |
rocket_midb, | |
rocket_mida, | |
rocket_topa, | |
rocket_topb, | |
rocket_bota, | |
rocket_botb, | |
rocket_botc, | |
rocket_topc, | |
rocket_wina, | |
rocket_winb, | |
rocket_fina, | |
rocket_finb, | |
rocket_fire | |
); | |
rocket.position.y = 0; | |
scene.add(rocket); | |
}; | |
const handleWindowResize = () => { | |
HEIGHT = window.innerHeight; | |
WIDTH = window.innerWidth; | |
renderer.setSize(WIDTH, HEIGHT); | |
camera.aspect = WIDTH / HEIGHT; | |
camera.updateProjectionMatrix(); | |
}; | |
const createLights = () => { | |
const ambientLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.25); | |
const directionalLight = new THREE.DirectionalLight(0xffcb87, 1); | |
directionalLight.position.set(-300, 300, 600); | |
const pointLight = new THREE.PointLight(0xff4f4f, 2, 1000, 2); | |
pointLight.position.set(-200, 100, 50); | |
scene.add(ambientLight, directionalLight, pointLight); | |
}; | |
const targetRocketPosition = 40; | |
const animationDuration = 2000; | |
const loop = () => { | |
const t = (Date.now() % animationDuration) / animationDuration; | |
renderer.render(scene, camera); | |
const delta = targetRocketPosition * Math.sin(Math.PI * 2 * t); | |
if (rocket) { | |
rocket.rotation.y += 0.01; | |
rocket.rotation.x += 0.01; | |
rocket.rotation.z += 0.01; | |
rocket.position.y = delta; | |
} | |
if (rocket_fire) { | |
rocket_fire.scale.set( | |
1 + delta / 100, | |
1 + Math.abs(delta / 100), | |
1 + delta / 100 | |
); | |
} | |
requestAnimationFrame(loop); | |
}; | |
const main = () => { | |
createScene(); | |
createLights(); | |
renderer.render(scene, camera); | |
loop(); | |
}; | |
main(); |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.151.3/three.min.js"></script> | |
<script src="https://unpkg.com/three@0.123.0/examples/js/loaders/GLTFLoader.js"></script> |
html, | |
body { | |
margin: 0; | |
height: 100%; | |
background: #03032b; | |
overflow: hidden; | |
perspective: 10rem; | |
} | |
#canvas { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
overflow: hidden; | |
} | |
.rain { | |
position: absolute; | |
width: 1rem; | |
height: 10rem; | |
background: #ffffff; | |
border-radius: 20%; | |
opacity: 0.4; | |
z-index: -1; | |
} | |
.drop { | |
width: 1rem; | |
height: 7rem; | |
background: #ffffff; | |
position: absolute; | |
border-radius: 20%; | |
opacity: 0.2; | |
} | |
.rain1 { | |
left: 5rem; | |
top: 2rem; | |
animation: raining 2s linear infinite both; | |
} | |
.rain2 { | |
left: 15rem; | |
top: 10rem; | |
animation: raining 3s linear infinite both; | |
} | |
.drop2 { | |
top: 12rem; | |
animation: raining 4s linear infinite both -2s; | |
} | |
.rain3 { | |
left: 5rem; | |
top: 35rem; | |
animation: raining 3s linear infinite both; | |
} | |
.rain4 { | |
right: 23rem; | |
top: 6rem; | |
animation: raining 4s linear infinite both; | |
} | |
.rain5 { | |
left: 25rem; | |
top: 47rem; | |
animation: raining 3s linear infinite both -3s; | |
} | |
.drop5 { | |
top: -6rem; | |
animation: raining 2s linear infinite both; | |
} | |
.rain6 { | |
right: 10rem; | |
top: 34rem; | |
animation: raining 3s linear infinite both; | |
} | |
.rain7 { | |
left: 34rem; | |
top: 10rem; | |
animation: raining 2s linear infinite both -5s; | |
} | |
.rain8 { | |
right: 25rem; | |
top: 40rem; | |
animation: raining 3s linear infinite both; | |
} | |
.drop8 { | |
top: -7rem; | |
animation: raining 4s linear infinite both -6s; | |
} | |
.rain9 { | |
right: 5rem; | |
top: 15.5rem; | |
animation: raining 3s linear infinite both; | |
} | |
.rain10 { | |
left: 24rem; | |
top: -4rem; | |
animation: raining 2s linear infinite both -3s; | |
} | |
.drop11 { | |
right: 17rem; | |
top: 20rem; | |
animation: raining 3s linear infinite both; | |
} | |
.drop12 { | |
right: 15rem; | |
top: 50rem; | |
animation: raining 4s linear infinite both -1s; | |
} | |
@keyframes raining { | |
from { | |
transform: translateY(-1200px); | |
} | |
to { | |
transform: translateY(869px); | |
} | |
} |
A Three.js rocket ship made from basic geometric shapes.
A Pen by Andrew Khassapov on CodePen.
Created Gist from CodePen Three.js rocket 🚀.