Three.js Geometri & Material — Creative Web

Three.js Geometri & Material Di Three.js, setiap objek 3D (Mesh) terdiri dari dua komponen: Geometry (bentuk) dan Material (tampilan permukaan). Built-in…

Di three.js, tiap benda yang tampil adalah Mesh = geometry (bentuknya: titik dan segitiga) + material (permukaannya: warna, tekstur, kilap). Bayangin membuat mug di bengkel keramik: cetakan tanah liatnya adalah geometry, glasir dan sablonnya adalah material. Dan seperti foto produk, glasir terbaik pun terlihat hitam di ruangan gelap: material yang realistis butuh cahaya.

Di lesson ini kita membuat produk baru Raka, Mug Sablon Kopi (Rp 65.000), dalam 3D: badan silinder, gagang setengah donat, dan sablon Kopi dari lesson Canvas API Dasar sebagai tekstur.

🧪 Tebakan cepat: mugnya sudah tampil dengan MeshBasicMaterial. Raka menggantinya dengan MeshStandardMaterial supaya terlihat lebih 3D, tapi belum menambah lampu apa pun. Mugnya jadi seperti apa? Tebak dulu, jawabannya ada di bagian "Cahaya".

Geometry: bentuk mug

Setup scene, kamera dan renderer sama dengan lesson Three.js Setup; bedanya kamera diangkat sedikit lalu diarahkan ke tengah dengan camera.lookAt(0, 0, 0), supaya permukaan kopinya kelihatan.

// Badan: CylinderGeometry(radiusAtas, radiusBawah, tinggi, segmenKeliling, segmenTinggi, terbuka, thetaStart)
const badan = new THREE.CylinderGeometry(0.8, 0.8, 1.6, 48, 1, false, Math.PI);

// Gagang: TorusGeometry(radius, tebalPipa, segmenPipa, segmenLingkar, busur)
const gagangGeo = new THREE.TorusGeometry(0.42, 0.1, 16, 32, Math.PI);

Material: Basic atau Standard

const mug = new THREE.Mesh(badan, [
  new THREE.MeshStandardMaterial({ map: tekstur, roughness: 0.5 }),  // 0: sisi, bersablon
  new THREE.MeshStandardMaterial({ color: '#c47a3d', roughness: 0.3 }), // 1: atas, kopi
  new THREE.MeshStandardMaterial({ color: '#1c1917', roughness: 0.5 }), // 2: dasar
]);
const gagang = new THREE.Mesh(gagangGeo, new THREE.MeshStandardMaterial({ color: '#1c1917', roughness: 0.5 }));
gagang.rotation.z = -Math.PI / 2;
gagang.position.x = 0.8;   // menempel di sisi kanan badan
mug.add(gagang);           // ikut berputar bersama mug
scene.add(mug);
MeshBasicMaterial MeshStandardMaterial
Menghitung cahaya gak, warnanya tampil apa adanya ya, PBR (physically based rendering)
Tanpa lampu tetap berwarna hitam
Parameter khas color, map, wireframe color, map, roughness (0 licin, 1 kasar), metalness (0 bukan logam, 1 logam)
Cocok untuk wireframe, debug, UI, gaya flat produk yang harus terlihat nyata

Bola merah MeshBasicMaterial({ color: 0xff0000 }) tampil merah rata dari sisi mana pun, ada lampu atau gak. Basic lebih ringan untuk GPU karena gak menghitung cahaya sama sekali.

Cahaya

scene.add(new THREE.AmbientLight('#ffffff', 0.8));    // dari segala arah, rata
const lampu = new THREE.DirectionalLight('#ffffff', 2.5); // sejajar, seperti matahari
lampu.position.set(3, 4, 5);                           // datang dari kanan atas depan menuju (0, 0, 0)
scene.add(lampu);

MeshStandardMaterial tanpa satu lampu pun gak memantulkan apa-apa: mugnya jadi siluet hitam pekat di atas latar krem. Itu jawaban tebakannya. Gak ada error; latar tetap tampil karena scene.background bukan benda yang disinari.

Sejak r155 intensitas cahaya di three.js dihitung secara fisik. DirectionalLight intensitas 1 yang menyinari tegak lurus cuma memberi sekitar sepertiga terang warna aslinya (1/π, dihitung di ruang warna linear), jadi tutorial lama yang ditulis sebelum r155 terlihat gelap. Panduan migrasinya: kalikan intensitas ambient dan directional lama dengan π (1 jadi sekitar 3). Di sini 2,5 dan 0,8.

Tekstur dari canvas

Sablon Kopi 300 × 300 kita taruh di canvas yang perbandingannya sama dengan keliling mug: keliling 2π × 0,8 = 5,03, tinggi 1,6, jadi lebarnya π kali tinggi: 942 × 300.

const kanvas = document.createElement('canvas');
kanvas.width = 942;
kanvas.height = 300;
const ctx = kanvas.getContext('2d');
ctx.fillStyle = '#1c1917';
ctx.fillRect(0, 0, 942, 300);
ctx.translate(321, 0);        // sablon 300 × 300 di tengah: (942 - 300) / 2
gambarSablonKopi(ctx);        // langkah-langkah dari lesson Canvas API Dasar

const tekstur = new THREE.CanvasTexture(kanvas);
tekstur.colorSpace = THREE.SRGBColorSpace;

Ruang warna

three.js menghitung cahaya di ruang warna linear, sedangkan hex seperti #c47a3d dan pixel canvas memakai sRGB. Warna hex di color dikonversi otomatis (ColorManagement aktif sejak r152). Tekstur gak bisa ditebak isinya, jadi kamu yang menandai: tekstur.colorSpace = THREE.SRGBColorSpace untuk tekstur warna (map). Tanpa itu, nilainya dianggap sudah linear dan jadi terlalu terang saat ditampilkan: dengan MeshBasicMaterial, hitam kaos #1c1917 keluar sebagai abu-abu #5d5855 dan sablonnya pudar. Tekstur data seperti normalMap atau roughnessMap tetap tanpa colorSpace. (Ini juga sebabnya segitiga three.js di lesson WebGL Konsep tengahnya sedikit lebih terang: campurannya dihitung di linear.)

dispose(): membersihkan memori GPU

Geometry, material dan tekstur yang sudah dikirim ke GPU gak dibersihkan garbage collector selama renderer masih menyimpannya. Saat mengganti geometry:

function gantiSegmen(n) {
  mug.geometry.dispose();   // buang yang lama dari memori GPU
  mug.geometry = new THREE.CylinderGeometry(0.8, 0.8, 1.6, n, 1, false, Math.PI);
  renderer.render(scene, camera);
}
console.log(renderer.info.memory.geometries); // 2: badan dan gagang

Tanpa dispose(), renderer.info.memory.geometries naik dari 2 jadi 4 setelah dua kali ganti segmen, dan terus naik. Material dan tekstur punya dispose() juga.

⚠️ Jebakan yang sering ketemu

TL;DR: Mesh = geometry + material, lalu scene.add. CylinderGeometry(rAtas, rBawah, tinggi, segmen, segmenTinggi, terbuka, thetaStart): segmen menentukan kehalusan (48 segmen = 192 segitiga, 8 = segi delapan), group 0 sisi, 1 atas, 2 bawah; TorusGeometry(radius, pipa, segPipa, segLingkar, arc), Math.PI untuk setengah lingkaran; BoxGeometry(l, t, d). MeshBasicMaterial gak menghitung cahaya (debug, flat, UI); MeshStandardMaterial (roughness, metalness) butuh cahaya, tanpa lampu jadi hitam. AmbientLight rata, DirectionalLight sejajar dari position ke (0, 0, 0); intensitas fisik sejak r155 (lama × π). Sablon canvas jadi CanvasTexture dengan colorSpace = THREE.SRGBColorSpace. Ganti geometry atau material? dispose() yang lama, cek renderer.info.memory.

Yang akan kamu pelajari