Referensi cepat Three.js untuk 3D web graphics. Scene, camera, renderer, geometry, materials, lights, animation, dan controls. Perfect buat developer yang bangun 3D visual di browser.
# npm
npm install three
# Import maps (browser, tanpa bundler)
# Pakai type="importmap" di HTML<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.170.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.170.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
</script>import * as THREE from 'three';
// 1. Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
scene.fog = new THREE.Fog(0x1a1a2e, 10, 50);
// 2. Camera
const camera = new THREE.PerspectiveCamera(
75, // FOV (degrees)
window.innerWidth / window.innerHeight, // aspect ratio
0.1, // near clipping plane
1000 // far clipping plane
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// 3. Renderer
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true, // transparent background
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
document.body.appendChild(renderer.domElement);
// 4. Animation loop
function animate() {
requestAnimationFrame(animate);
// Update objects here
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
// 5. Handle resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});// Box
const box = new THREE.BoxGeometry(1, 1, 1); // width, height, depth
const box2 = new THREE.BoxGeometry(2, 2, 2, 4, 4, 4); // segmented
// Sphere
const sphere = new THREE.SphereGeometry(1, 32, 32); // radius, widthSeg, heightSeg
// Plane
const plane = new THREE.PlaneGeometry(10, 10, 10, 10);
// Cylinder
const cylinder = new THREE.CylinderGeometry(1, 1, 2, 32); // topR, botR, height, seg
// Cone
const cone = new THREE.ConeGeometry(1, 2, 32);
// Torus (donut)
const torus = new THREE.TorusGeometry(1, 0.3, 16, 100); // radius, tube, radialSeg, tubularSeg
// Ring
const ring = new THREE.RingGeometry(1, 2, 32);
// Buffer Geometry (custom)
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1, -1, 0, // vertex 1
1, -1, 0, // vertex 2
0, 1, 0, // vertex 3
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.computeVertexNormals();// Modify geometry attributes
const geo = new THREE.SphereGeometry(1, 32, 32);
// Get position attribute
const positions = geo.attributes.position;
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i);
const y = positions.getY(i);
const z = positions.getZ(i);
// Modify vertex
positions.setZ(i, z + Math.sin(x * 5) * 0.1);
}
positions.needsUpdate = true;
geo.computeVertexNormals();// Basic (unlit, flat color)
const basicMat = new THREE.MeshBasicMaterial({
color: 0x44aa44,
wireframe: false,
transparent: true,
opacity: 0.8,
});
// Lambert (diffuse, no specular)
const lambertMat = new THREE.MeshLambertMaterial({
color: 0xff0000,
emissive: 0x110000,
});
// Phong (specular highlights)
const phongMat = new THREE.MeshPhongMaterial({
color: 0x00ff00,
specular: 0xffffff,
shininess: 100,
flatShading: false,
});
// Standard (PBR: metalness + roughness)
const stdMat = new THREE.MeshStandardMaterial({
color: 0x2194ce,
metalness: 0.7, // 0 = dielectric, 1 = metal
roughness: 0.3, // 0 = mirror, 1 = fully diffuse
envMapIntensity: 1.0,
});
// Physical (advanced PBR)
const physMat = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
metalness: 0.5,
roughness: 0.2,
clearcoat: 1.0, // glossy coating
clearcoatRoughness: 0.1,
transmission: 0.9, // glass-like
thickness: 0.5,
ior: 1.5, // index of refraction
});
// Normal (show normals as colors, debug)
const normalMat = new THREE.MeshNormalMaterial();
// Texture material
const texture = new THREE.TextureLoader().load('texture.jpg');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
const texMat = new THREE.MeshStandardMaterial({
map: texture, // diffuse color map
normalMap: normalTexture, // surface detail
roughnessMap: roughTexture, // surface roughness
metalnessMap: metalTexture, // metallic areas
});// Ambient (global illumination)
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
// Directional (sun-like, parallel rays)
const directional = new THREE.DirectionalLight(0xffffff, 1.0);
directional.position.set(5, 10, 5);
directional.castShadow = true;
directional.shadow.mapSize.width = 2048;
directional.shadow.mapSize.height = 2048;
directional.shadow.camera.near = 0.5;
directional.shadow.camera.far = 50;
directional.shadow.camera.left = -10;
directional.shadow.camera.right = 10;
directional.shadow.camera.top = 10;
directional.shadow.camera.bottom = -10;
scene.add(directional);
// Point (omnidirectional, like a bulb)
const point = new THREE.PointLight(0xff9000, 1.0, 10, 2); // color, intensity, distance, decay
point.position.set(0, 3, 0);
point.castShadow = true;
scene.add(point);
// Spot (cone of light)
const spot = new THREE.SpotLight(0xffffff, 1.0, 20, Math.PI / 6, 0.5, 1);
spot.position.set(0, 10, 0);
spot.target.position.set(0, 0, 0);
scene.add(spot);
scene.add(spot.target);
// Hemisphere (sky + ground)
const hemi = new THREE.HemisphereLight(0x87ceeb, 0x36290e, 0.6); // skyColor, groundColor
scene.add(hemi);
// RectArea (flat panel light)
import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js';
RectAreaLightUniformsLib.init();
const rect = new THREE.RectAreaLight(0xffffff, 5, 4, 2); // color, intensity, width, height
rect.position.set(0, 5, 0);
rect.lookAt(0, 0, 0);
scene.add(rect);// Create mesh (geometry + material)
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 1, 0); // x, y, z
mesh.rotation.set(0, Math.PI / 4, 0); // Euler angles
mesh.scale.set(1, 1, 1);
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
// Group multiple objects
const group = new THREE.Group();
const cube = new THREE.Mesh(boxGeo, mat);
cube.position.x = -2;
const sphere = new THREE.Mesh(sphereGeo, mat);
sphere.position.x = 2;
group.add(cube, sphere);
group.position.y = 1;
scene.add(group);
// Clone mesh
const mesh2 = mesh.clone();
mesh2.position.x = 3;
// Traverse scene
scene.traverse((obj) => {
if (obj.isMesh) {
obj.rotation.x += 0.01;
}
});
// Dispose (memory management)
mesh.geometry.dispose();
mesh.material.dispose();
scene.remove(mesh);const camera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1000);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);
// FOV zoom
camera.fov = 30;
camera.updateProjectionMatrix();const aspect = window.innerWidth / window.innerHeight;
const frustumSize = 10;
const camera = new THREE.OrthographicCamera(
-frustumSize * aspect / 2, // left
frustumSize * aspect / 2, // right
frustumSize / 2, // top
-frustumSize / 2, // bottom
0.1, // near
1000 // far
);import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 5;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2; // prevent below ground
controls.autoRotate = true;
controls.autoRotateSpeed = 1.0;
controls.target.set(0, 1, 0);
// Must update in animation loop when damping is enabled
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
import { FlyControls } from 'three/addons/controls/FlyControls.js';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
// TransformControls (gizmo for editing)
const transformControl = new TransformControls(camera, renderer.domElement);
transformControl.addEventListener('dragging-changed', (e) => {
orbitControls.enabled = !e.value;
});
scene.add(transformControl);
transformControl.attach(mesh);
// Switch modes
transformControl.setMode('translate'); // 'translate' | 'rotate' | 'scale'const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsed = clock.getElapsedTime();
const delta = clock.getDelta();
// Rotate
mesh.rotation.y += delta * 0.5;
// Bounce
mesh.position.y = Math.sin(elapsed * 2) * 0.5 + 1;
// Scale pulse
const scale = 1 + Math.sin(elapsed * 3) * 0.1;
mesh.scale.set(scale, scale, scale);
controls.update();
renderer.render(scene, camera);
}
animate();import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('model.glb', (gltf) => {
const model = gltf.scene;
scene.add(model);
// Play animation
const mixer = new THREE.AnimationMixer(model);
const clips = gltf.animations;
// Play first clip
const action = mixer.clipAction(clips[0]);
action.play();
// Play specific clip by name
const walk = mixer.clipAction(THREE.AnimationClip.findByName(clips, 'Walk'));
walk.play();
// Store mixer for update
animMixers.push(mixer);
});
// Update in animation loop
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
animMixers.forEach(mixer => mixer.update(delta));
renderer.render(scene, camera);
requestAnimationFrame(animate);
}import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load(
'model.glb',
(gltf) => {
const model = gltf.scene;
model.position.set(0, 0, 0);
model.scale.set(1, 1, 1);
scene.add(model);
},
(progress) => {
console.log(`Loaded: ${(progress.loaded / progress.total * 100).toFixed(0)}%`);
},
(error) => {
console.error('Load error:', error);
}
);const textureLoader = new THREE.TextureLoader();
// Single texture
textureLoader.load('diffuse.jpg', (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
material.map = texture;
material.needsUpdate = true;
});
// Load manager (track multiple)
const manager = new THREE.LoadingManager();
manager.onLoad = () => console.log('All loaded');
manager.onProgress = (url, loaded, total) => console.log(`${loaded}/${total}`);
manager.onError = (url) => console.error('Error:', url);
const loader = new THREE.TextureLoader(manager);
loader.load('tex1.jpg');
loader.load('tex2.jpg');const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
window.addEventListener('click', (event) => {
// NDC coordinates (-1 to 1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
// Check intersection with objects
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const hit = intersects[0];
console.log('Hit:', hit.object.name);
console.log('Point:', hit.point);
console.log('Distance:', hit.distance);
console.log('Face:', hit.face); // { a, b, c, normal }
}
});
// Ray from custom origin
raycaster.set(
new THREE.Vector3(0, 0, 0), // origin
new THREE.Vector3(0, 0, -1) // direction
);import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
0.8, // strength
0.4, // radius
0.85 // threshold
);
composer.addPass(bloomPass);
// Render with composer instead of renderer
function animate() {
requestAnimationFrame(animate);
controls.update();
composer.render();
}// 1. Use InstancedMesh untuk banyak object identik
const instanced = new THREE.InstancedMesh(geometry, material, 1000);
const dummy = new THREE.Object3D();
for (let i = 0; i < 1000; i++) {
dummy.position.set(
Math.random() * 100 - 50,
Math.random() * 100 - 50,
Math.random() * 100 - 50,
);
dummy.updateMatrix();
instanced.setMatrixAt(i, dummy.matrix);
}
scene.add(instanced);
// 2. Frustum culling (otomatis, tapi bisa di-disable)
mesh.frustumCulled = true;
// 3. Merge geometries
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
const merged = mergeGeometries([geo1, geo2, geo3]);
// 4. LOD (Level of Detail)
import { LOD } from 'three';
const lod = new LOD();
lod.addLevel(highDetailMesh, 0); // visible when distance < 10
lod.addLevel(mediumDetailMesh, 10); // visible when 10-30
lod.addLevel(lowDetailMesh, 30); // visible when > 30
scene.add(lod);
// 5. Pixel ratio cap
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// 6. Dispose unused resources
geometry.dispose();
material.dispose();
texture.dispose();Login atau daftar akun gratis untuk membaca cheat sheet ini.