Selama ini kita cuma pakai BoxGeometry buat bikin kubus. Sekarang kita kenalan sama
geometry-geometry lainnya, tapi sebelum itu kita harus paham dulu geometry itu
sebenarnya apa.
Apa itu Geometry?
Di ThreeJS, geometry itu tersusun dari dua hal:
- Vertices, adalah titik-titik koordinat di ruang 3D. Bayangin kaya paku-paku yang kita tancepin di udara, satu paku itu namanya vertex (tunggal), banyaknya namanya vertices (jamak).
- Faces, adalah segitiga-segitiga yang menghubungkan titik-titik tadi sampai jadi permukaan. Jadi paku-paku tadi kita hubungkan pakai kain segitiga, nah kain itulah yang kelihatan sebagai bentuk objek kita.
Kenapa segitiga? Karena tiga titik itu pasti membentuk satu bidang datar. Kalau empat titik, bisa aja salah satunya kepeleset ke atas dikit dan bidangnya jadi bengkok, GPU bakal bingung. Makanya semua bentuk di 3D, sebulat apapun, ujung-ujungnya tetap kumpulan segitiga.
Geometry biasanya kita pakai untuk bikin mesh, tapi nanti geometry juga bisa dipakai untuk bikin particles, di mana satu vertex jadi satu partikel. Itu urusan bab lain.
Dan yang disimpan di dalam vertex bukan cuma posisi. Nanti kita juga bakal simpan UV coordinates (buat naruh tekstur) dan normals (buat ngitung arah cahaya), tapi itu juga bahasan bab-bab berikutnya.
Geometry Bawaan ThreeJS
ThreeJS punya banyak banget geometry bawaan. Kamu gak perlu hafal cara bikin satu-satu, cukup tau aja mereka ada, jadi pas butuh tinggal buka dokumentasinya. Kebanyakan halaman dokumentasinya juga sudah ada contoh yang bisa diutak-atik.
Satu hal penting: semua geometry bawaan ini turunan dari BufferGeometry. Class
induk ini punya banyak method bawaan kaya translate(...), rotateX(...),
normalize(), dan lain-lain, cuma di catatan ini gak kita pakai.
- BoxGeometry, untuk bikin kotak/kubus.
- PlaneGeometry, untuk bikin bidang datar persegi panjang. Ini yang biasanya dipakai jadi lantai.
- CircleGeometry, untuk bikin lingkaran penuh atau sepotong lingkaran, kaya potongan pie chart.
- ConeGeometry, untuk bikin kerucut atau sepotong kerucut. Alasnya bisa kita tutup atau kita biarin bolong.
- CylinderGeometry, untuk bikin tabung. Tutup atas-bawahnya bisa dibuka atau ditutup, dan radius tiap ujungnya bisa beda, jadi bisa dipakai bikin gelas juga.
- RingGeometry, untuk bikin cincin datar atau sepotong cincin datar. Kaya donat yang kegencet, gak punya ketebalan.
- TorusGeometry, untuk bikin cincin yang punya ketebalan, ini donat beneran, atau sepotong donat.
- TorusKnotGeometry, untuk bikin bentuk simpul yang ruwet itu. Sering dipakai orang buat pamer material karena permukaannya melengkung ke mana-mana.
- DodecahedronGeometry, untuk bikin bola dengan 12 sisi. Bisa ditambah details biar makin bulat.
- OctahedronGeometry, untuk bikin bola dengan 8 sisi. Bisa ditambah details juga.
- TetrahedronGeometry, untuk bikin bola dengan 4 sisi. Kalau details-nya gak dinaikin ya jangan harap bulat, bentuknya masih limas segitiga.
- IcosahedronGeometry, untuk bikin bola yang segitiga-segitiganya ukurannya kurang lebih sama rata semua.
- SphereGeometry, ini bola yang paling sering dipakai orang, permukaannya kelihatan kaya kotak-kotak (quads). Quad itu cuma nama lain dari dua segitiga yang digabung.
- ShapeGeometry, untuk bikin bentuk datar mengikuti sebuah garis/path.
- TubeGeometry, untuk bikin pipa yang jalannya ngikutin sebuah path.
- ExtrudeGeometry, untuk “menebalkan” sebuah path jadi 3D. Kaya adonan kue yang kita cetak terus kita tarik ke atas. Ketebalan dan lengkungan pinggirnya (bevel) bisa kita atur.
- LatheGeometry, untuk bikin vas bunga atau sepotong vas. Prinsipnya kaya gerabah yang diputar di atas roda pembentuk, satu garis diputar 360 derajat jadi bentuk 3D.
- TextGeometry, untuk bikin teks 3D. Syaratnya kita harus nyediain font-nya
dalam format typeface json, bukan
.ttfbiasa.
Kalau bentuk yang kamu mau gak ada di daftar itu, ada dua jalan: bikin sendiri pakai JavaScript (nanti kita coba di bagian bawah catatan ini), atau bikin di software 3D kaya Blender terus di-export dan di-import ke project kita. Yang kedua itu bahasan bab lain.
Contoh: BoxGeometry
Kita udah pernah bikin kubus, tapi kita belum ngobrolin parameternya. Hampir semua geometry punya parameter, dan selalu cek dokumentasinya dulu sebelum dipakai, karena urutan parameternya beda-beda tiap geometry.
BoxGeometry punya 6 parameter:
- width, ukuran di sumbu x.
- height, ukuran di sumbu y.
- depth, ukuran di sumbu z.
- widthSegments, jumlah pembagian di sumbu x.
- heightSegments, jumlah pembagian di sumbu y.
- depthSegments, jumlah pembagian di sumbu z.
Tiga yang pertama gampang, itu ukuran. Tiga yang terakhir ini yang sering bikin bingung.
1. Segments alias Subdivisions
Subdivisions itu seberapa banyak segitiga yang menyusun satu sisi. Bawaannya
1, artinya satu sisi cuma dibentuk dari 2 segitiga (dua segitiga digabung jadi satu
kotak, ingat quad tadi).
Kalau kita naikin jadi 2, satu sisi jadi disusun 8 segitiga:
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2)Masalahnya, kalau dijalanin sekarang kita gak bisa lihat bedanya sama sekali. Kubusnya ya kubus aja, permukaannya rata merah semua.
2. Wireframe
Biar segitiganya kelihatan, kita kasih wireframe: true di material-nya. Wireframe
bakal nampilin garis-garis pembatas tiap segitiga, jadi objeknya kelihatan kaya
kerangka kawat:
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })Sekarang baru kelihatan, memang benar ada 8 segitiga di tiap sisi.
Perlu dicatat, wireframe itu properti material, bukan geometry. Salah taruh
bakal diem aja gak error:
❌ salah, BoxGeometry gak kenal wireframe, jadi gak ada efeknya
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2, { wireframe: true })
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })✅ benar, wireframe masuk ke material
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })3. Kenapa Segments Itu Penting
Di kubus, nambah segments itu keliatannya sia-sia, wong sisinya datar, mau dibelah jadi 2 atau 200 segitiga hasilnya tetap kotak datar.
Bedanya baru kerasa di bentuk melengkung kaya bola:
const geometry = new THREE.SphereGeometry(1, 32, 32)Makin banyak subdivisions, makin gak kelihatan patahan antar sisinya, bolanya makin
mulus. Coba turunin ke new THREE.SphereGeometry(1, 4, 4), nanti bentuknya lebih mirip
bola sepak jadul yang kotak-kotak daripada bola beneran.
Tapi jangan langsung napsu ngasih angka gede. Makin banyak vertices dan faces, makin berat kerja GPU. Logikanya sama kaya kita naruh gambar 8K buat foto profil yang tampilnya cuma sebesar jempol, hasilnya sama aja tapi laptopnya ngos-ngosan. Cari angka paling kecil yang hasilnya udah kelihatan mulus.
Bikin Geometry Sendiri (BufferGeometry)
Kadang bentuk yang kita mau gak ada di daftar bawaan. Kalau bentuknya rumit banget dan
detail, mending bikin di software 3D (bahasan bab lain), tapi kalau bentuknya masih
sederhana, kita bisa rakit sendiri pakai BufferGeometry.
Kita mulai dari yang paling sederhana: satu segitiga.
1. Bikin BufferGeometry Kosong
// Bikin BufferGeometry kosong
const geometry = new THREE.BufferGeometry()Sekarang dia bener-bener kosong, gak ada vertex sama sekali, jadi belum ada yang bisa dilihat.
2. Siapkan Float32Array
Untuk masukin vertices ke BufferGeometry, kita harus mulai dari Float32Array.
Float32Array itu typed array bawaan JavaScript, bukan punya ThreeJS. Bedanya
sama array biasa ada dua:
- Isinya cuma boleh angka desimal (float), gak bisa dicampur string atau objek.
- Panjangnya sudah dipatok dari awal, gak bisa
pushseenaknya.
Kenapa harus ribet gini? Karena array model begini bentuknya di memori itu rapat dan seragam, jadi bisa dikirim ke GPU apa adanya tanpa diterjemahin dulu. Array biasa mah isinya bisa apa aja, GPU-nya gak ngerti.
Cara pertama, tentukan dulu panjangnya, isi belakangan:
const positionsArray = new Float32Array(9)
// Vertex pertama
positionsArray[0] = 0
positionsArray[1] = 0
positionsArray[2] = 0
// Vertex kedua
positionsArray[3] = 0
positionsArray[4] = 1
positionsArray[5] = 0
// Vertex ketiga
positionsArray[6] = 1
positionsArray[7] = 0
positionsArray[8] = 0Cara kedua, langsung kasih array-nya:
const positionsArray = new Float32Array([
0, 0, 0, // Vertex pertama
0, 1, 0, // Vertex kedua
1, 0, 0 // Vertex ketiga
])Kenapa 9 angka buat 1 segitiga? Karena 1 segitiga = 3 vertex, dan 1 vertex = 3 angka (x, y, z). Jadi 3 × 3 = 9.
Yang penting dipahami: array ini rata, satu dimensi. Gak ada kurung-kurung per vertex, gak ada penanda apapun. Kita nulisnya berurutan: x-y-z vertex pertama, terus x-y-z vertex kedua, terus x-y-z vertex ketiga, begitu seterusnya.
❌ salah, Float32Array gak nerima array bersarang kaya gini
const positionsArray = new Float32Array([
[0, 0, 0],
[0, 1, 0],
[1, 0, 0]
])✅ benar, semua angka ditulis berderet dalam satu array datar
const positionsArray = new Float32Array([
0, 0, 0,
0, 1, 0,
1, 0, 0
])3. Ubah Jadi BufferAttribute
Array tadi belum bisa langsung dikasih ke BufferGeometry, harus dibungkus dulu jadi
BufferAttribute.
Parameter pertama adalah typed array-nya, parameter kedua adalah berapa angka yang
dihitung jadi satu vertex. Karena tadi kita bilang satu posisi vertex itu terdiri
dari 3 angka (x, y, dan z), maka isinya 3:
const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3)Angka 3 inilah yang jadi aturan bacanya. Dia yang ngasih tau ThreeJS supaya baca
array datar tadi tiga-tiga. Kalau kita isi 2, ThreeJS bakal baca dua-dua dan
bentuknya jadi ngawur total, dan gak bakal ada pesan error, cuma hasilnya aneh.
Ini salah satu bug yang paling bikin pusing karena keliatannya kodenya bener semua.
4. Pasang ke Geometry Pakai setAttribute
Terakhir, kita tempelin attribute tadi ke geometry pakai method setAttribute(...).
Parameter pertama itu nama attribute-nya, parameter kedua nilainya:
geometry.setAttribute('position', positionsAttribute)Kenapa namanya harus 'position' persis? Karena shader internal ThreeJS nyari nama
itu buat nentuin letak vertices-nya. Ini bukan nama bebas yang bisa kita karang.
Bahasan shader ada di bab jauh nanti, tapi untuk sekarang cukup tau kalau nama ini
harga mati.
❌ salah, ThreeJS gak nemu 'position', objeknya gak bakal muncul
geometry.setAttribute('posisi', positionsAttribute)✅ benar
geometry.setAttribute('position', positionsAttribute)Dan faces-nya? Gak perlu kita bikin. Segitiganya otomatis dibentuk mengikuti urutan vertices tadi, tiap tiga vertex jadi satu segitiga.
5. Semuanya Digabung
// Bikin BufferGeometry kosong
const geometry = new THREE.BufferGeometry()
// Bikin Float32Array berisi posisi vertices (tiga-tiga)
const positionsArray = new Float32Array([
0, 0, 0, // Vertex pertama
0, 1, 0, // Vertex kedua
1, 0, 0 // Vertex ketiga
])
// Bikin attribute-nya dan kasih nama 'position'
const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3)
geometry.setAttribute('position', positionsAttribute)Jalanin, dan muncullah satu segitiga hasil karya kita sendiri.
Bikin Segitiga Acak Sebanyak-banyaknya
Kalau satu segitiga bisa, banyak segitiga juga bisa. Kita bikin 50 segitiga dengan posisi acak:
// Bikin BufferGeometry kosong
const geometry = new THREE.BufferGeometry()
// Bikin 50 segitiga (450 angka)
const count = 50
const positionsArray = new Float32Array(count * 3 * 3)
for(let i = 0; i < count * 3 * 3; i++)
{
positionsArray[i] = (Math.random() - 0.5) * 4
}
// Bikin attribute-nya dan kasih nama 'position'
const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3)
geometry.setAttribute('position', positionsAttribute)Yang bikin dahi berkerut paling cuma count * 3 * 3, padahal gampang banget:
count= 50 segitiga yang kita mau,× 3karena satu segitiga punya 3 vertex,× 3lagi karena satu vertex punya 3 angka (x, y, z).
Jadi totalnya 50 × 3 × 3 = 450 angka. Bukan 50, bukan 150, tapi 450.
Bagian (Math.random() - 0.5) * 4 juga sama akal-akalannya kaya waktu kita bikin
kursor di bab kamera kemarin. Math.random() cuma ngasih angka 0 sampai 1, semua
positif, jadi semua segitiganya bakal numpuk di satu pojok. Dikurangi 0.5 biar
nilainya jadi -0.5 sampai 0.5, artinya titik nolnya pindah ke tengah. Dikali 4
biar sebarannya lebih lebar, dari -2 sampai 2.
Kalau mau lebih rapat, kecilin pengalinya. Kalau mau lebih berantakan ke mana-mana, gedein.
Index
Ada satu hal menarik lagi dari BufferGeometry: kita bisa pakai ulang vertex yang
sama lewat properti index.
Coba bayangin kubus. Vertex di pojok-pojoknya itu dipakai bareng oleh beberapa sisi sekaligus. Kalau diperhatiin lebih teliti lagi, hampir tiap vertex dipakai bareng sama segitiga-segitiga tetangganya.
Kalau gak pakai index, tiap segitiga nulis ulang koordinat vertex-nya sendiri-sendiri,
padahal titiknya persis sama. Boros. Dengan index, koordinatnya cukup ditulis sekali,
terus segitiganya tinggal nunjuk “aku pakai titik nomor 0, 3, dan 5”. Hasilnya array
kita jadi lebih kecil dan performanya lebih enteng.
Kita gak bahas cara pakainya di catatan ini, cukup tau dulu kalau fasilitas ini ada.
Ringkasan
- Geometry itu kumpulan vertices (titik koordinat 3D) dan faces (segitiga yang menghubungkan titik-titik itu jadi permukaan). Semuanya segitiga, karena tiga titik pasti membentuk bidang datar.
- Vertex bisa nyimpen lebih dari sekadar posisi, nanti ada UV coordinates buat tekstur dan normals buat cahaya.
- ThreeJS punya belasan geometry bawaan dari
BoxGeometrysampaiTextGeometry, dan semuanya turunanBufferGeometry. Gak perlu dihafal, cukup tau ada, terus buka dokumentasinya pas butuh. - Selalu cek dokumentasi buat urutan parameternya, karena tiap geometry beda-beda.
segments/ subdivisions itu jumlah segitiga penyusun satu sisi. Bawaannya1yang berarti 2 segitiga per sisi, kalau diisi2jadi 8 segitiga per sisi.- Pakai
wireframe: truedi material kalau mau lihat segitiganya. Ingat, itu properti material, bukan geometry. - Makin banyak vertices makin mulus, tapi makin berat. Di bentuk datar kaya kubus, nambah segments itu percuma; yang butuh justru bentuk melengkung kaya bola.
- Bikin geometry sendiri urutannya:
BufferGeometrykosong →Float32Array→BufferAttribute→setAttribute('position', ...). Float32Arrayitu array datar satu dimensi, ditulis berderet x-y-z, x-y-z, x-y-z, gak boleh bersarang.- Angka
3diBufferAttributeitu aturan baca tiga-tiga. Salah angka gak bakal error, cuma bentuknya ngawur. - Namanya wajib
'position', karena shader internal ThreeJS nyari nama itu. count * 3 * 3artinya jumlah segitiga × 3 vertex × 3 angka per vertex.(Math.random() - 0.5) * 4dipakai biar nilai acaknya nyebar ke arah negatif juga, sama kaya trik posisi kursor di bab kamera.indexdipakai buat berbagi vertex antar segitiga biar array-nya lebih kecil dan performanya lebih baik, tapi belum kita pakai sekarang.
Bab selanjutnya kita akan pakai Debug UI biar bisa utak-atik nilai-nilai ini langsung dari layar tanpa bolak-balik ngedit kode.