Three.js

Three.js is a rich JavaScript library for computer graphics representation on browser using WebGL. Visit their documentation and examples top understand better.

Computer Graphics Concepts

Vertex

A vertex is a point in 3D space, defined by its x, y, and z coordinates. Multiple vertices are used to create polygons or meshes that form the geometry of 3D objects.

Rasterization

The process of converting 3D models into a 2D image by mapping the 3D scene onto a 2D viewport (like your screen). This step converts polygons into pixels or fragments.

Texture

Textures are images applied to the surface of a geometry to give it more detail or realism (e.g., brick textures on a wall). You can use textures for color, bump mapping, or normal mapping to create the illusion of depth.

Shaders

Shaders are small programs that run on the GPU (Graphics Processing Unit) to control how vertices and pixels are processed and rendered.

  • Vertex Shader: Transforms 3D coordinates into 2D screen coordinates
  • Fragment/Pixel Shader: Determines the color and appearance of individual pixels

Light Tracing

A form of ray tracing, where light rays are simulated as they interact with surfaces. Light tracing focuses on how light enters the scene from various sources and is traced through multiple reflections and refractions for realistic lighting effects.

Three.js Common Components

Getting started

Install Three.js developement kit, import or link the CDN to use Three.js

javascript
import * as THREE from "https://cdn.skypack.dev/three@0.132.2";
import { OrbitControls } from "https://cdn.skypack.dev/three@0.132.2/examples/jsm/controls/OrbitControls.js";
html
<script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.163.0/examples/jsm/"
    }
  }
</script>
<script type="module" src="example.js">
  // Three JS goes here without 'src' attribute
</script>

The example code below includes many elements in detail and is long

Example Three.js Code
example.js
import * as THREE from "https://cdn.skypack.dev/three@0.132.2";
import { OrbitControls } from "https://cdn.skypack.dev/three@0.132.2/examples/jsm/controls/OrbitControls.js";

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(15, 16, 13);
camera.lookAt(scene.position);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;   // Enable Shadow
document.body.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);
controls.update();

// Geometry + Material = Mesh
const planeGeometry = new THREE.PlaneGeometry(20, 20);
const planeMaterial = new THREE.MeshPhongMaterial({ color: 0xcccccc, side: THREE.DoubleSide });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -0.5 * Math.PI;
plane.position.y = 0;
scene.add(plane);

const colors = [0xff0000, 0x0088aa, 0xaaaaaa, 0xdddd00]; // Red, blue, grey, yellow
const cl = colors.length;

const materials = [
    new THREE.MeshBasicMaterial({ side: THREE.DoubleSide }),
    new THREE.MeshPhongMaterial({
        specular: 0xffffff, shininess: 100,
        side: THREE.DoubleSide // for drawing the inside of the tube
    }),
    new THREE.MeshStandardMaterial({
        metalness: 0.9, roughness: 0.4,
        side: THREE.DoubleSide
    }),
    new THREE.MeshLambertMaterial({
        wireframe: true, wireframeLinewidth: 2,
        side: THREE.DoubleSide
    })
];
const ml = materials.length;

const geometries = [
    new THREE.SphereGeometry(1, 16, 8),
    new THREE.ConeGeometry(1, 2, 16),
    new THREE.BoxGeometry(1.5, 1.5, 1.5),
    new THREE.CylinderGeometry(1, 1, 2, 16),
    new THREE.RingGeometry(0.3, 1, 16),
    new THREE.TorusGeometry(1, 0.5, 16, 16),
    new THREE.TorusKnotGeometry(1, 0.3, 128, 8),
    new THREE.IcosahedronGeometry(1),
    new THREE.DodecahedronGeometry(1),
    new THREE.OctahedronGeometry(1),
    new THREE.TetrahedronGeometry(1),
    // new THREE.CapsuleGeometry(1, 1, 4, 16)
    // Custom geometries:
    // EdgesGeometry
    // ExtrudeGeometry: create 3D using 2D shape
    // LatheGeometry: based on f(x) on x axis
    // PolyhedronGeometry: specifying vertexs
    // ShapeGeometry: create 2D shape
    // TubeGeometry: extrude along 3D curve
    // WireframeGeometry: create 2D shape
]

var items = []
for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
        var geometry = geometries[(i * 4 + j) % geometries.length];
        var material = materials[rand(ml)];
        var item = new THREE.Mesh(geometry, material);
        var pos = grid_pos(i, j);
        item.position.set(pos[0], pos[1], pos[2]);
        item.material.color.set(colors[rand(cl)]);
        item.castShadow = true;
        scene.add(item);
        items.push(item);
    }
}

const distance = 50;
const angle = Math.PI / 16;
const penumbra = 0.5;
const decay = 0.1;

const spotLight = new THREE.SpotLight(0xffffff, 1, distance, angle, penumbra, decay);
spotLight.position.set(10, 20, -20);
spotLight.castShadow = true;
scene.add(spotLight);

const ambientLight = new THREE.AmbientLight(0x101010);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0x886200, 1);
directionalLight.position.set(10, 20, 20);
directionalLight.castShadow = true;
scene.add(directionalLight);

// const hemisphereLight = new THREE.HemisphereLight(0x4040ff, 0xffff80, 1.0);
// hemisphereLight.position.set(-10, 20, 20);
// hemisphereLight.castShadow = true;
// scene.add(hemisphereLight);

const light = new THREE.PointLight(0xE11584, 1, 100);
light.position.set(0, 5, 0);
light.castShadow = true;
scene.add(light);

const rectAreaLight = new THREE.RectAreaLight(0xff0000, 1.0, 200, 200);
rectAreaLight.position.set(-10, 20, -20);
rectAreaLight.lookAt(0, 0, 0);
scene.add(rectAreaLight);


const gridHelper = new THREE.GridHelper(10, 10);
scene.add(gridHelper);

const yGrid = new THREE.GridHelper(10, 10);
yGrid.rotation.x = Math.PI / 2; // Rotate the grid to align with the Y-axis
scene.add(yGrid);

const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

const cameraHelper = new THREE.CameraHelper(camera);
scene.add(cameraHelper);

const spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);

const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5);
scene.add(directionalLightHelper);

const pointLightHelper = new THREE.PointLightHelper(light);
scene.add(pointLightHelper);

var ypos = [];
for (let i = 0; i < 16; i++) {
    ypos.push(Math.random());
}

var animate = function () {
  requestAnimationFrame(animate);
  // required if controls.enableDamping or controls.autoRotate are set to true

  var time = performance.now() * 0.001; // Get current time in seconds
  var amplitude = 1;
  var frequency = 1;
  for (var i = 0; i < items.length; i++) {
      var yOffset = 2 + Math.sin(time * frequency + ypos[i] * 10) * amplitude; // Calculate y offset based on sine function
      items[i].rotation.y += 0.01;
      items[i].position.y = yOffset;
  }

  controls.update();
  renderer.render(scene, camera);
}

function grid_pos(x, y) {
  return [(x - 2) * 4 + 2, 1, (y - 2) * 4 + 2];
}

function rand(n) {
  return Math.floor(Math.random() * n);
}

function handleResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

animate();
window.addEventListener('resize', handleResize, false);

Results

Geometry

Geometry defines the shape of the 3D object through vertices, faces, and edges.

Material

Materials define how the surface of a mesh looks when rendered, including its color, reflectivity, texture, and lighting response.

MaterialDescription
MeshBasicMaterialA material that doesn’t react to light
MeshStandardMaterialSupports lighting and reflections, used for realistic rendering
MeshPhongMaterialIncludes specular highlights for shiny surfaces
MeshLambertMaterialFor non-shiny surfaces, without specular highlights
MeshNormalMaterialMaps the normal vectors to RGB colors
MeshPhysicalMaterialExtension of the MeshStandardMaterial, providing more advanced physically-based rendering properties
MeshDepthMaterialThe closer to the camera, the brigther it is
MeshToonMaterialA material implementing toon shading

Helper

Helper helps visualize light, directions, camera and position of elements.

Light

TypesDescription
AmbientLightGeneral light that illuminates all objects equally
DirectionalLightLike sunlight, casts parallel rays from a specific direction
HemisphereLightDifferent light source from sky and ground
PointLightA light that emits in all directions from a point (like a light bulb)
SpotLightA cone-shaped light that shines in a specific direction
RectAreaLightSimulate light source from a window

Loader

User loader to load tools into the model

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

Amazing Three.js examples

Reflection

Advanced Applications

Procedual-GL.js

Procedural GL JS is a library for creating 3D map experiences on the web, written in JavaScript and WebGL. It is built on top of THREE.js.

Path Tracing

This is an amzing real-time PathTracing with global illumination and progressive rendering, all on top of the Three.js WebGL framework.