Three.js Summary

conveyGhostconveyGhost
8 min read

This article aims to explain how to create 3D objects using Three.js and clarify shader notation. It is not intended as an explanatory article, so please think of it as a memo only. All code is written under the assumption that the threejs module has been installed via npm.

Creating 3D objects using Three.js

Template (with comments)

// Import modules (adjust paths according to your environment)
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import GUI from "lil-gui";

/*
// Define the canvas
*/
const canvas = document.getElementById("canvas"); 

/*
// Define sizes
*/
const sizes = {
  width: innerWidth,
  height: innerHeight,
}

/*
// Define the scene
*/
const scene = new THREE.Scene();

/*
// Camera
*/
// By default, the camera is positioned at the origin and facing in the negative Z direction
const fov = 75; // Field of view (degree)
const aspect = sizes.width / sizes.height; // Aspect ratio
const near = 0.1; // Near clipping plane
const far = 3000; // Far clipping plane
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far); // Define the camera
scene.add(camera); // Add the camera to the scene
camera.position.z = 2; // Change the camera position

/*
// Define the renderer
*/
const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
  antialias: true, // Apply anti-aliasing
});
renderer.setSize(sizes.width, sizes.height); // Set size
renderer.setPixelRatio(window.devicePixelRatio); // Set pixel ratio

// Define the geometry
const geometry = new THREE.BoxGeometry(1, 1, 1);

// Define the material (Some materials are affected by light, and some are not)
const material = new THREE.MeshToonMaterial();

// Create the mesh
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh); // Add the mesh to the scene

/*
// Define the lights
*/
// The default position of the light is at the origin
const light = new THREE.DirectionalLight('white', 1);
scene.add(light); // Add the light to the scene
light.position.set(-1, 2, 4);

const color = 0xFFFFFF;
const intensity = 0.2;
const ambientLight = new THREE.AmbientLight(color, intensity);
scene.add(ambientLight);

// Render the scene
renderer.render(scene, camera);

/*
// Implement UI debugging
*/
const gui = new GUI(); // Define lil-gui for UI debugging

/*
// Enable mouse camera control
*/
const orbitControls = new OrbitControls(camera, renderer.domElement); // Define OrbitControls for mouse control

/*
// Handle browser resize
*/
window.addEventListener("resize", () => {
  sizes.width = window.innerWidth;
  sizes.height = window.innerHeight;

  camera.aspect = sizes.width / sizes.height;
  camera.updateProjectionMatrix();

  renderer.setSize(sizes.width, sizes.height);
  renderer.setPixelRatio(window.devicePixelRatio);
});

// Define the animation loop
function animate(time) {
  time *= 0.001; // Convert elapsed time from ms to seconds
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

animate();

Template (Minimal Comments, Simplified Description)

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import GUI from "lil-gui";

let camera, scene, renderer;
const clock = new THREE.Clock();

// Define sizes
const sizes = {
  width: innerWidth,
  height: innerHeight,
};

init();
animate();

function init() {
  const canvas = document.getElementById("canvas");

  // Define the scene
  scene = new THREE.Scene();

  // Define the camera
  const fov = 75,
    aspect = sizes.width / sizes.height,
    near = 0.1,
    far = 3000;
  camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = -2;
  scene.add(camera);

  // Define the lights
  const light = new THREE.DirectionalLight("white", 1);
  const ambientLight = new THREE.AmbientLight("white", 1);
  scene.add(light, ambientLight);

  // Define the material
  const material = new THREE.MeshToonMaterial();

  // Define the geometry
  const geometry = new THREE.BoxGeometry(1, 1, 1);

  // Create the mesh
  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  // Define the renderer
  renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true, // Apply anti-aliasing
  });
  renderer.setSize(sizes.width, sizes.height); // Set size
  renderer.setPixelRatio(window.devicePixelRatio); // Set pixel ratio
  renderer.render(scene, camera);

  // Enable camera control with mouse
  const orbitControls = new OrbitControls(camera, renderer.domElement);
  orbitControls.minDistance = 2;
  orbitControls.maxDistance = 30;

  // Handle resize
  window.addEventListener('resize', onWindowResize);
}

// Implement UI debugging
function setUpGui() {
  const gui = new GUI();
}

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

// Animation loop function
function animate() {
  requestAnimationFrame(animate);
  render();
}

// Rendering function for animation
function render() {
  const delta = clock.getDelta();
  // Any time-based processes
  renderer.render(scene, camera);
}

Three.js Customization

Camera Control

// Change the camera's position (default is at the origin)
camera.position.set(x, y, z);
camera.position.x = 1;

// Change the field of view (FOV)
camera.fov = 60;

// Change the direction the camera is looking (default is x=0, y=0, negative z-axis)
// Look towards the origin
camera.lookAt(new THREE.Vector3(0, 0, 0));

Adding Fog

Write the start and end distance for the camera to match. Objects between these distances will appear more faded the further away they are, using the specified color.

// Set the fog
// new THREE.Fog(color, near distance, far distance);
scene.fog = new THREE.Fog(0x000000, 50, 2000);

Material Manipulation

// Texture
import textureImage from "./texture/texture.jpg";
let texture = new THREE.TextureLoader().load(textureImage);

const material = new THREE.MeshBasicMaterial({
  color: 'red', // Specify color
  transparent: true, // Enable transparency
  opacity: 0.5, // Set opacity
  side: THREE.DoubleSide, // Render both sides
  map: texture // Set the texture
});

Manipulating Mesh (3D Object)

// Change the size
mesh.scale.set(x, y, z);
mesh.scale.x = 2;

// Rotation
mesh.rotation.set(x, y, z);
mesh.rotation.x = 2;

// Positioning (translation)
mesh.position.set(x, y, z);
mesh.position.x = 2;

// Duplicate (clone)
const mesh2 = mesh.clone();

Light Manipulation

// Adjust the light position
light.position.set(x, y, z);
light.position.x = 5;

// Change the light color
light.color.set(0xff0000);

Renderer Manipulation

// Set the background color
renderer.setClearColor(new THREE.Color(0xEEEEEE));

// Enable transparency
renderer = new THREE.WebGLRenderer({
  alpha: true,
});
// Make the canvas background transparent
renderer.setClearColor(0x000000, 0);

renderer.setSize(sizes.width, sizes.height); // Specify size
renderer.setPixelRatio(window.devicePixelRatio); // Specify pixel ratio
lil-gui Settings
// Define lil-gui
const gui = new GUI();

// Debug color changes
gui.addColor(material, "color");

// Usage of add
gui.add(valueToDebug, min, max, step).name("Label");

// Debug position
gui.add(box.position, "x", -10, 10, 1).name("translateX");

// Element visibility
gui.add(box, "visible").name("box");
gui.add(light, "visible").name("light");

// Define a folder
const position = gui.addFolder('Position');

// Add position debug controls to the folder
position.add(box.position, "x", -10, 10, 1);

Cast shadows on objects

// Enable shadow rendering
renderer.shadowMap.enabled = true;

// Enable shadows for the light
light.castShadow = true;

// Allow the mesh to receive shadows
meshFloor.receiveShadow = true;

// Allow the object to cast shadows
box.castShadow = true;

// Set shadow resolution (power of 2)
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;

Grouping Objects

// Define a group
const wrap = new THREE.Group(); 
wrap.add(mesh1, mesh2); // Add any Object3D 
scene.add(wrap);

Getting Coordinates

// Get world coordinates
// object3D is any 3D object.
const world = object3D.getWorldPosition(new THREE.Vector3());

Cube Environment Mapping

// Import images at the top of the JavaScript file
import envTop from "./envMap/top.png";
import envBottom from "./envMap/bottom.png";
import envRight from "./envMap/right.png";
import envLeft from "./envMap/left.png";
import envFront from "./envMap/front.png";
import envBack from "./envMap/back.png";

// Store in the envimages array
const urls = [envRight, envLeft, envTop, envBottom, envBack, envFront];

// Perform environment cube mapping and specify the mapping mode
const textureCube = new THREE.CubeTextureLoader().load(urls);
textureCube.mapping = THREE.CubeRefractionMapping;
scene.background = textureCube;

Mirror Finish

Environment mapping setup is required.

Glass texture with a filled appearance:

You can set this with MeshLambertMaterial, MeshPhongMaterial, and MeshBasicMaterial.
With MeshPhysicalMaterial, you can set it by increasing the thickness.

Hollow glass texture:
You can set it with MeshPhysicalMaterial.

// Reuse cube environment mapping code

const crystal = new THREE.MeshLambertMaterial({
  color: 0xf5f5f5,
  envMap: textureCube, // Apply environment map
  refractionRatio: 0.9, // Refraction ratio (default: 0.98)
  reflectivity: 0.9, // Reflectivity (default: 1)
});

// This can be configured without cube environment mapping
const vidro = new THREE.MeshPhysicalMaterial({
  transmission: 1, // Transmission (transparency)
  metalness: 0.1, // Metalness
  roughness: 0.1, // Roughness
  ior: 1.2, // Index of refraction (IOR)
  thickness: 0.1, // Thickness
  specularIntensity: 1, // Specular intensity (amount of reflection)
  specularColor: 0xff7f50, // Specular color (reflection color)
});

Introduction of Blender Model

I haven't really used Blender, so I'm not sure about it.

From here, assuming installation with .ply data.

import { PLYLoader } from "three/examples/jsm/loaders/PLYLoader";

// Define the PLYLoader
const loader = new PLYLoader();

// Load the model
loader.load("./ply/Model_ply.ply", function (geometry) {
  const model = new THREE.Mesh(geometry, material);
  scene.add(model);
});

Operations Using BufferGeometry

Draw a Line

// Define vertices
const points = [];
points.push(new THREE.Vector3(-10, 0, 0));
points.push(new THREE.Vector3(0, 10, 0));
points.push(new THREE.Vector3(10, 0, 0));

// Define the geometry
const geometry = new THREE.BufferGeometry().setFromPoints(points);

// Define the material
const material = new THREE.LineBasicMaterial({ color: 0x0000ff });

// Create the line (mesh)
const line = new THREE.Line(geometry, material);

// Add to the scene
scene.add(line);

Basic Shader Notation

// Import the texture
import textureImage from "./texture/texture.jpg";

// Define the texture
let texture = new THREE.TextureLoader().load(textureImage);

// Define ShaderMaterial
const material = new THREE.ShaderMaterial({
  vertexShader: `
  uniform float uFrequency; // Receive variable from the JS file
  varying vec2 vUv; // Pass variable

  void main() {
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);

    vUv = uv; // Store UV coordinates

    vec4 viewPosition = viewMatrix * modelPosition;
    vec4 projectionPosition = projectionMatrix * viewPosition;
    gl_Position = projectionPosition;
  }
  `,
  fragmentShader: `
  uniform vec3 uColor;
  uniform sampler2D uTexture;
  varying vec2 vUv; // Receive variable
    void main(){
    // gl_FragColor = vec4(uColor, 1.0);
    vec4 textureColor = texture2D(uTexture, vUv);
    gl_FragColor = vec4(textureColor);
  }
  `,
  side: THREE.DoubleSide,
  uniforms: {
    uFrequency: { value: 10.0 },
    uColor: { value: new THREE.Color('red') },
    uTexture: { value: texture },
  }
});

Common GLSL Functions

texture2D

vec4 texture2D (sampler2D, vec2)

sampler2D: The texture from which you want to extract color
vec2: The UV coordinates from which you want to extract texel information

Thank you for reading my article carefully.
Best.

conveyGhost.

5
Subscribe to my newsletter

Read articles from conveyGhost directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

conveyGhost
conveyGhost

conveGhost m a Frontend developer | UI / UX designer. Also I provide web3 frontend for various blockchain ecosystems. I use Figma, Canva, Adobe XD for web design, and have rich exp with Next.js,React.js. Also use GSAP, babylon.js, Three.js and other js libraries for animation effects, and more immersive web app. If you have some trouble in that works, and plan to start work, pls feel free to reach out me for collab with me. You can contact me via discord (conveyghost) Thank you