An In-Depth Guide to Three.js: Building 3D Models from Scratch

Rownok Bosunia
7 min readAug 28, 2024

--

Introduction to Three.js

Three.js is a powerful JavaScript library that simplifies the creation of 3D graphics in a web browser. It abstracts the complexities of WebGL, allowing developers to create immersive 3D experiences with minimal code. Whether you’re building simple 3D models or complex interactive scenes, Three.js provides the tools you need to get started.

Why Use Three.js?

  • Cross-browser compatibility: Three.js ensures your 3D scenes work across all modern browsers.
  • Easy to learn: The library abstracts WebGL, making it more accessible to developers.
  • Extensive documentation and community support: With a large community, Three.js offers abundant resources, tutorials, and examples.
  • Versatile: You can create anything from simple shapes to complex interactive environments.

Setting Up Three.js

Before diving into creating 3D models, you need to set up your development environment.

Installation

There are multiple ways to include Three.js in your project:

  1. Using a CDN: The simplest way is to include Three.js via a CDN in your HTML file.
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

2. Using npm: If you’re working with a module bundler like Webpack, install Three.js via npm:

npm install three

Basic Structure of a Three.js Scene

To understand how Three.js works, it’s essential to know the basic components of a 3D scene:

  1. Scene: This is where all objects, lights, and cameras are placed.
  2. Camera: Defines what part of the scene is visible to the user.
  3. Renderer: Renders the scene from the camera’s perspective onto the canvas.
  4. Mesh: The combination of geometry (shape) and material (appearance).
  5. Light: Illuminates the scene, allowing objects to be visible.

Example: Setting Up a Basic Scene

Here’s a basic example to get you started:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js Basic Scene</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// Create a scene
const scene = new THREE.Scene();// Create a camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// Create a renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Add a cube
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Rotate the cube
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>

In this example:

  • A scene is created using THREE.Scene().
  • A perspective camera is set up with a field of view of 75 degrees.
  • A WebGL renderer is used to render the scene.
  • A simple cube is created using BoxGeometry and MeshBasicMaterial.
  • The cube is rotated in an animation loop using requestAnimationFrame.

Creating 3D Models with Three.js

Working with Geometry

Three.js provides several built-in geometries you can use to create 3D models. Some of the most commonly used geometries include:

  • BoxGeometry: A rectangular box.
  • SphereGeometry: A sphere.
  • PlaneGeometry: A flat plane.
  • CylinderGeometry: A cylinder.
  • TorusGeometry: A donut-shaped ring.

Example: Creating a Sphere

const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);

This code creates a red sphere with a radius of 1 and 32 segments, which determines the smoothness of the sphere.

Custom Geometry

For more complex shapes, you might need to create custom geometry using THREE.BufferGeometry. This allows you to define the vertices and faces manually.

Example: Custom Triangle

const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
const triangle = new THREE.Mesh(geometry, material);
scene.add(triangle);

Materials

Materials define the appearance of the objects in your scene. Three.js provides a variety of materials, such as:

  • MeshBasicMaterial: A simple material that doesn’t react to light.
  • MeshLambertMaterial: A material that reacts to light but without specular highlights.
  • MeshPhongMaterial: A material that includes specular highlights, making it shiny.
  • MeshStandardMaterial: A physically-based material that provides more realistic rendering.

Example: Using MeshStandardMaterial

const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.5
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

In this example, MeshStandardMaterial gives the cube a metallic and rough appearance.

Textures

You can enhance your 3D models by applying textures. Textures are images mapped onto the surface of your geometry.

Example: Applying a Texture

const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('path/to/texture.jpg');
const material = new THREE.MeshBasicMaterial({ map: texture });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

Lighting

Lighting is crucial in 3D scenes as it affects how materials are displayed. Three.js offers several types of lights:

  • AmbientLight: A global light that illuminates all objects equally.
  • PointLight: A light source that emits light in all directions from a single point.
  • DirectionalLight: A light that emits parallel rays, similar to sunlight.
  • SpotLight: A light source that emits light in a cone shape.

Example: Adding Ambient and Point Lights

const ambientLight = new THREE.AmbientLight(0x404040); // Soft white light
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(10, 10, 10);
scene.add(pointLight);

Advanced Techniques:

Loading 3D Models

While you can create models using basic geometries, you might want to import more complex models from external sources. Three.js supports loading models in various formats such as .obj, .fbx, and .gltf.

Example: Loading a GLTF Model

const loader = new THREE.GLTFLoader();
loader.load('path/to/model.gltf', function (gltf) {
scene.add(gltf.scene);
}, undefined, function (error) {
console.error(error);
});

Animations

Three.js provides tools to animate objects. You can use the built-in THREE.Clock to handle time-based animations or import keyframe animations from external models.

Example: Simple Animation

const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
cube.rotation.x += delta * 0.5;
cube.rotation.y += delta * 0.5;
renderer.render(scene, camera);
}
animate();

Camera Controls

For interactive scenes, you might want to allow users to control the camera. The THREE.OrbitControls library provides an easy way to add mouse-based controls.

Example: Adding Orbit Controls

const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;

Post-Processing

Post-processing effects can significantly enhance the visual quality of your 3D scenes by adding effects like bloom, depth of field, and motion blur. Three.js provides a powerful post-processing pipeline using THREE.EffectComposer, which allows you to chain multiple effects together.

Example: Adding Bloom Effect

const composer = new THREE.EffectComposer(renderer);
const renderPass = new THREE.RenderPass(scene, camera);
composer.addPass(renderPass);
const bloomPass = new THREE.BloomPass(1.25); // Intensity of the bloom
composer.addPass(bloomPass);
function animate() {
requestAnimationFrame(animate);
composer.render(); // Use composer to render the scene with post-processing
}
animate();

In this example, the bloom effect is applied after rendering the scene, giving objects with bright areas a glowing appearance.

Shadows

Shadows add depth and realism to your scene. In Three.js, you can enable shadows by configuring both the lights and the renderer.

Example: Enabling Shadows

// Enable shadows in the renderer
renderer.shadowMap.enabled = true;
// Create a spotlight that casts shadows
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(10, 10, 10);
spotLight.castShadow = true;
scene.add(spotLight);
// Enable shadow casting and receiving on objects
cube.castShadow = true;
cube.receiveShadow = true;
const planeGeometry = new THREE.PlaneGeometry(200, 200);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
scene.add(plane);

Here, shadows are enabled for both the renderer and the objects in the scene. The plane beneath the cube receives the shadow cast by the cube, adding realism to the scene.

Physics Integration

For more interactive and dynamic scenes, integrating physics can be crucial. Three.js can work with physics engines like Ammo.js, Cannon.js, or Oimo.js to simulate real-world physics such as gravity, collisions, and rigid body dynamics.

Example: Basic Physics with Cannon.js

// Set up Cannon.js world
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0); // Earth's gravity
// Create a physics body for the cube
const shape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
const body = new CANNON.Body({ mass: 1 });
body.addShape(shape);
body.position.set(0, 10, 0);
world.addBody(body);
function animate() {
requestAnimationFrame(animate);
// Step the physics world
world.step(1/60);
// Update Three.js mesh position with physics simulation
cube.position.copy(body.position);
cube.quaternion.copy(body.quaternion);
renderer.render(scene, camera);
}
animate();

In this example, the cube’s position and rotation are controlled by the physics simulation, allowing it to fall under gravity and collide with other objects.

Working with Large Scenes

As your scene grows more complex, performance can become a concern. Three.js offers several strategies to optimize large scenes.

Tips for Optimizing Performance

  1. Level of Detail (LOD): Use LOD techniques to reduce the complexity of distant objects.
  2. Frustum Culling: Ensure that objects outside the camera’s view are not rendered.
  3. Instancing: Use THREE.InstancedMesh to efficiently render many identical objects.
  4. Texture Optimization: Use compressed textures and reduce texture resolution where possible.
  5. Efficient Geometry: Simplify geometry where detail isn’t necessary, and merge geometries when possible.

Exporting Your 3D Models

Once your 3D model is complete, you might want to export it for use in other applications or share it online.

Example: Exporting as GLTF

Three.js allows you to export your scene or models in the GLTF format, which is widely supported and optimized for web applications.

const exporter = new THREE.GLTFExporter();
exporter.parse(scene, function (gltf) {
const blob = new Blob([JSON.stringify(gltf)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'scene.gltf';
a.click();
});

This script allows you to download your entire scene as a .gltf file, which can then be imported into other 3D software or used directly in web applications.

Conclusion

Three.js is an incredibly versatile library that empowers developers to create stunning 3D graphics in the browser. From setting up a basic scene to implementing advanced features like physics, lighting, and post-processing effects, Three.js provides all the tools you need to bring your 3D visions to life.

Whether you’re a beginner starting with simple shapes or an advanced developer working on complex, interactive environments, the flexibility and power of Three.js make it an essential tool in the web developer’s toolkit. With its extensive documentation, active community, and continuous development, Three.js will continue to be a leading choice for web-based 3D graphics.

Now that you’ve learned the basics, it’s time to experiment and build your own 3D models and scenes. The only limit is your imagination. Happy coding!

This detailed guide should serve as a comprehensive introduction to Three.js and the various options it offers. As you dive deeper, you’ll discover even more advanced techniques and possibilities with this powerful library.

--

--

Rownok Bosunia
Rownok Bosunia

Written by Rownok Bosunia

As a self-taught software developer, I've been on a journey to learn, grow and innovate in this ever-evolving field. Sharing my insights and experiences.

No responses yet