Three.js · Geometri
Custom BufferGeometry (özel geometri oluşturma)
Ham vertex verisini siz yazın, GPU çizer
Three.js’in sunduğu hazır şekiller (küp, küre vb.) çoğu zaman yeterli olsa da, bazen doğada
bulunmayan, tamamen matematiksel verilerle tanımlanan veya dinamik olarak değişen formlara ihtiyaç
duyarız. Custom BufferGeometry, GPU’ya giden ham vertex verisini sizin
yazdığınız Float32Array ve setAttribute ile yönetmenizi sağlar; “şekil
sınıfı seç” yerine “diziyi sen üret” moduna geçersiniz.
Bu sayfa özel geometri pratiğine odaklanır: neden primitive yetmez, attribute
kanallarını bir arada kurmak, indeksli / indekssiz karşılaştırma, canlı demolar ve dinamik
güncelleme (needsUpdate). Attribute ve buffer dilinin temeli
Attributes & buffer’lar
konusundadır; hazır formlar ve segment seçimi
Primitive geometriler
konusuna bırakılır; burada tekrar etmiyoruz.
Genel geometri çerçevesi (vertex, yüzey, dispose) için Geometri giriş; indeks ayrıntısı Indexed vs non-indexed konusunda. Aşağıda Vertex Playground, attribute toggle, indeks split demoları ve wireframe kod örneği bulunur.
Neden özel geometri?
Hazır geometriler (BoxGeometry, SphereGeometry …) belirli
matematiksel kalıplara bağlıdır: genişlik, yarıçap, segment gibi parametreleri verirsiniz,
Three.js vertex ve üçgen listesini sizin yerinize üretir (
Primitive geometriler
konusu bu modeli anlatır). Özel bir BufferGeometry ise o listeyi siz
yazarsınız; kütüphane “şekil sınıfı” seçmez, GPU’ya gidecek ham diziyi siz doldurursunuz.
Bu geçiş her projede gerekli değildir; prototipte küp ve küre çoğu zaman yeterlidir. Aşağıdaki üç avantaj, primitive’in yetmediği veya pahalıya geldiği durumlarda özel geometriye neden geçildiğini özetler.
- Tam kontrol: Her bir noktanın (vertex) konumunu siz belirlersiniz.
Silüet kütüphanede yoksa (spiral, veri noktası bulutu, import sonrası düzenlenmiş mesh)
tek yol budur. İsteğe bağlı
normal,uv,colorvesetIndexde aynı geometri üzerinde sizin kararınızdır — kanal rolleri Attributes sayfasında, bir arada kurma bu sayfanın §2’sinde. - Performans: Yalnızca ihtiyacınız olan veriyi (attribute) GPU’ya
göndererek bellek kullanımını optimize edersiniz; gereksiz kanal taşımazsınız.
Paylaşılan köşelerde aynı
positionsatırını her üçgende tekrarlamak yerinesetIndexile dört köşe + indeks listesi kullanabilirsiniz — kare örneği indeksli bölüm ve split demoda. Segment / triangle bütçesi tartışması primitive tarafında kalır; burada odak “hangi buffer’lar gidecek ve köşe paylaşılacak mı” sorusudur. - Esneklik: Arazi (terrain), veri görselleştirme veya prosedürel nesne
üretimi doğrudan dizi yazımıyla yapılır; formu her kare güncelleyerek dalga, gürültü veya
morph benzeri efektler elde edersiniz. CPU’daki diziyi değiştirdikten sonra GPU’yu
haberdar etmek için sayfa sonundaki
needsUpdatenotuna bakın; canlı örnek Vertex Playground demosunda.
Ne zaman primitive, ne zaman özel?
Kutu, küre, düzlem veya silindir yeterliyse primitive kullanın; parametrik modelleme ve segment seçimi primitiv mantığı konusunda. Özel geometriye, veri kaynağınız zaten bir dizi olduğunda (yükseklik haritası, CSV noktaları, simülasyon çıktısı) veya primitive’in üreteceği topoloji istediğiniz şey değilken geçin. Genel vertex / dispose çerçevesi Geometri giriş sayfasındadır; özel geometriye geçmeden önce setAttribute kalıbını oturtun, sonra aşağıdaki demolarla ölçün.
Geometrinin atomları: BufferAttributes
GPU, köşeleri ve yüzeyleri anlamak için attribute (öznitelik) adı verilen
veri dizilerine ihtiyaç duyar. Primitive kullandığınızda bu dizileri Three.js üretir; özel
bir geometride genelde Float32Array olarak siz hazırlar, ardından
BufferAttribute ile setAttribute üzerinden geometriye bağlarsınız.
Typed array ve itemSize ayrıntısı
Attributes · Typed Arrays
konusundadır; burada kanalların özel geometride ne anlama geldiğine odaklanıyoruz.
Standart kanallar (özel geometri)
Aynı vertex indeksinde hizalanan kanallar birlikte okunur: 5. köşenin konumu, normali ve UV’si aynı satır numarasına denk gelmelidir. Tam tanımlar ve shader tarafı standart kanallar konusunda; özet liste:
- position: Noktaların
[x, y, z]koordinatları. Olmazsa olmazdır — mesh çizilemez. Özel geometride ilk yazdığınız kanal budur; uzunlukköşe sayısı × 3olmalıdır. - normal: Işığın yüzeyden nasıl yansıyacağını belirleyen yön vektörleri.
Elle yazmadıysanız
computeVertexNormals()çoğu basit ağ için yeterlidir;MeshStandardMaterialgibi ışığa duyarlı materyaller normal bekler. - uv: 2D dokuların yüzeye nasıl sarılacağını belirleyen
[u, v]koordinatları. Düz renkte şart değil; desen veya PBR dokusu için eklenir — sarma mantığı UV mapping konusundadır. - color: Her köşeye özel renk vermek için
[r, g, b]değerleri (isteğe bağlı). MateryaldevertexColors: trueaçılmadan etkisi görünmez; indeks split demosunda ve attribute toggle demosunda açıp kapatabilirsiniz.
Minimum kurulum akışı
Boş BufferGeometry → position dizisi →
setAttribute
→ (gerekirse) uv, color →
computeVertexNormals() veya elle normal → (isteğe bağlı)
setIndex. §1’deki performans notu: paylaşımlı köşe için indeks buffer’ı
§5
konusunda.
Vertex Playground önce yalnızca position ile çalışır; attribute toggle demo ise
aynı ızgarada normal, UV ve vertex color’ın görünüme etkisini yan yana gösterir; kanalı
eklemek ile materyal ayarını açmak arasındaki farkı orada test edebilirsiniz.
Dizi ne işe yarıyor? Vertex Playground
Float32Array içindeki her üçlü, bir köşenin (x, y, z)
koordinatıdır.
Aşağıdaki ızgara düzleminde segment sayısı arttıkça köşe sayısı büyür; GPU
bu diziyi okuyarak üçgenleri çizer. Görünümü nokta, tel kafes veya dolu yüzey olarak
değiştirerek aynı verinin nasıl yorumlandığını görebilirsiniz; gürültü ve dalga seçenekleri
dizinin animasyonla güncellenmesine örnek teşkil eder.
Ölçüler için demo sabitleri tablosuna bakın.
İsteğe bağlı ses: demo kutusundan ayrı sütun genişliğinde şerit; dosya adı
Custom-Buffer-Geometry-Demo-1 (uzantı sırasıyla denenir).
Özet: Segment = ızgara yoğunluğu; köşe sayısı ≈ (segment+1)².
position dizisi köşe sayısı × 3 uzunluğundadır; yani bu sayfada
gördüğünüz şekil, doğrudan bu sayıların yorumlanmasıdır.
Demo sabitleri tablosu (Vertex Playground)
Bu blok diagram-custom-buffer-geometri.js içindeki
initVertexPlayground ile eşlenir. Düzlem PlaneGeometry üzerinde
baseRest saklanır; gürültü/dalga position buffer’ını her kare veya
yeniden kurulumda günceller.
| Sahne / rol | Parametre | Değer | Tür |
|---|---|---|---|
| Ortam | setClearColor |
0x05060c |
🔒 Sabit |
| Düzlem | PlaneGeometry(w, h, seg, seg) |
w = h = 5 · sonra rotateX(-π/2) |
🔒 Sabit |
| Yerleşim | PLAYGROUND_MESH_Y |
0.75 |
🔒 Sabit |
| UI segment | data-cbuffer-segments |
min 3 · max 40 · varsayılan 16 |
↔ HTML |
| Nokta modu | PointsMaterial.size |
0.09 |
🔒 Sabit |
| Dalga | sin/cos terimleri |
bx*1.8 + t*1.6 · genlik 0.45 ·
bz*2.1 + t*1.2 · genlik 0.32
|
🔒 Sabit |
| Gürültü | hash11 karışımı |
iki örnek · toplam ölçek ≈ ±0.65 (yarım genlik) |
🔒 Sabit |
| Zaman | tAnim += |
0.016 / kare |
🔒 Sabit |
Önemli kod kesiti
Taban köşeler baseRest içinde saklanır; applyDisplacement aynı
geometride position ve isteğe bağlı color attribute’unu yazar.
const g = new THREE.PlaneGeometry(w, h, seg, seg);
g.rotateX(-Math.PI / 2);
// baseRest[i] = dünya köşesi (bx, by, bz)
if (useNoise) {
dy += (hash11(i * 17.1 + bz * 3.2) - 0.5) * 0.85
+ (hash11(i * 31.7 - bx * 2.1) - 0.5) * 0.45;
}
if (useWave) {
dy += Math.sin(bx * 1.8 + tAnim * 1.6) * 0.45
+ Math.cos(bz * 2.1 + tAnim * 1.2) * 0.32;
}
pos.setXYZ(i, bx, by + dy, bz);
geoRef.computeVertexNormals();
Attribute anahtarları: normal, renk, UV
GPU bir öznitelik olmadan o kanaldaki hesabı yapamaz: normal yoksa
yönlü ışık yok; color yoksa köşe başına renk yok; uv yoksa
doku sarılamaz. Aşağıda normal için MeshStandardMaterial (PBR) ile
MeshBasicMaterial (ışıksız düz ton) arasında geçiş yapıyoruz; fark
bilinçli
olarak sertleştirildi. position her zaman gereklidir; arayüzde kilitli.
Rakamlar demo sabitleri tablosunda
initAttributeToggle ile satır satır eşlenir.
İsteğe bağlı ses: demo kutusundan ayrı sütun genişliğinde şerit; dosya adı
Custom-Buffer-Geometry-Demo-2 (uzantı sırasıyla denenir).
Normal kapalı = Basic (sahne ışıkları görünmez); normal açık = Standard (gölgeli yüzey). Renk açıkken altta kırmızı / üstte mavi köşe gradient’i; UV açıkken dama tahtası dokusu; kapalıyken doku kaybolur.
Demo sabitleri tablosu (Attribute toggle)
initAttributeToggle: yüksek segmentli küre üzerinde vertex
color gradient’i; makeCheckerTexture ile
CanvasTexture + map.repeat(5,5). Normal açıkken
MeshStandardMaterial, kapalıyken MeshBasicMaterial.
| Sahne / rol | Parametre | Değer | Tür |
|---|---|---|---|
| Küre | SphereGeometry |
yarıçap 1.15 · segment 56 × 56 |
🔒 Sabit |
| Dama dokusu | makeCheckerTexture |
ızgara 10×10 hücre · canvas 256² ·
repeat(5, 5)
|
🔒 Sabit |
| PBR | MeshStandardMaterial |
metalness: 0.22 · roughness: 0.42 ·
vertexColors: true
|
🔒 Sabit |
| Işık | ortam + yönlü ×2 | AmbientLight(0xffffff, 0.28) · ana
DirectionalLight(0xfff0e0, 1.05) konum
(4, 7, 5) · dolgu 0xaaccff yoğunluk
0.45 konum (-5, 2, -4)
|
🔒 Sabit |
| UI | normal / uv / color |
varsayılan üçü de açık; position kilitli |
↔ HTML |
Önemli kod kesiti
Malzeme sınıfı normal bayrağına göre değişir; map yalnızca UV açıkken
atanır.
function syncMaterialProps(mat, wantC, wantUv) {
mat.vertexColors = wantC;
mat.map = wantUv ? checkerTex : null;
if (!wantC) {
mat.color.set(0x8899bb);
} else {
mat.color.set(0xffffff);
}
mat.needsUpdate = true;
}
function applyAttrs() {
const wantN = !!(nOn && nOn.checked);
const wantC = !!(cOn && cOn.checked);
const wantUv = !!(uvOn && uvOn.checked);
if (wantN) {
geo.computeVertexNormals();
}
mesh.material = wantN ? matStandard : matBasic;
syncMaterialProps(matStandard, wantC, wantUv);
syncMaterialProps(matBasic, wantC, wantUv);
}
İndeksli (indexed) vs. indekssiz geometri
Bir kareyi iki üçgenle çizmek için GPU’ya altı köşe göndermek gerekir; oysa dünyada yalnızca
dört benzersiz köşe vardır. Primitive’ler çoğu zaman indeksli buffer üretir; özel geometride
bu ayrımı siz kurarsınız: setIndex ile paylaşım, veya indekssiz listede her
üçgen için ayrı position satırları. İki düzen de geçerlidir; fark, veriyi
nasıl paketlediğinizdedir; görünür silüet aynı kalabilir.
Kare örneği: altı kayıt, dört köşe
İndekssiz karede position buffer’ında altı satır vardır: ortak köşeler (örneğin
sağ üst) iki üçgende iki kez yazılır; dünya konumu aynı, buffer indeksi farklı. İndeksli
tarafta dört satır + index dizisi (0, 1, 2 ve 0, 2, 3)
üçgenleri bu dört köşeye bağlar. Ham position baytı kabaca
6×3×4 B’ye karşı 4×3×4 B (+ indeks buffer) düşer; büyük ağlarda bu
fark katlanır. Sayıları canlı görmek için
split demo
ve
kod kesiti
aşağıdadır.
İki düzen
- İndekssiz: Paylaşılan köşeyi her üçgende yeniden yazarsınız; kod basit, “her üçgen üç ardışık vertex” okuması doğrudandır. Bellek ve bant genişliği büyüyebilir; köşe başına farklı normal veya vertex color istediğinizde (keskin kenar, farklı UV köşesi) bazen bilinçli tercih edilir — split demo sol panel.
- İndeksli: Dört köşe +
indexbuffer (“0,1,2” ve “0,2,3”); büyük modellerde bellek tasarrufı belirgin olabilir. Paylaşılan köşedenormal/uvtek kayıt taşır — yumuşak yüzey için uygundur; keskin ayrım gerekiyorsa indekssiz veya vertex bölme gerekir. Split demo sağ panel;Uint16Array/Uint32Array,toNonIndexed()ve GPU önbelleği Indexed vs non-indexed konusundadır.
Ne zaman hangi düzen?
Hangi düzeni seçeceğiniz vertex sayısı, güncelleme sıklığı ve köşe başına attribute
ihtiyacına bağlıdır. Statik, paylaşımlı köşeli büyük mesh’lerde indeksli düzen genelde
önceliklidir; hızlı prototip, tek seferlik çizim veya üçgen başına farklı köşe verisi
gerektiğinde indekssiz liste yeterli olabilir. Animasyonda yalnızca position
güncelliyorsanız indeks listesi sabit kalabilir; her köşeyi ayrı oynatıyorsanız indekssiz
akış bazen daha az kafa karıştırıcıdır.
Bu sayfada kare üzerinden görsel karşılaştırma yapıyoruz; genel karar çerçevesi, bellek
formülleri ve küp demosu advanced sayfada. §2’deki minimum akışta setIndex son
isteğe bağlı adımdır; önce position (ve gerekli kanallar) oturmalıdır.
Yan yana: indeks bellek karşılaştırması
Aynı kare için iki veri düzeni: indekssiz tarafta her üçgen kendi köşe
kopyasını taşır (aynı dünya konumunda farklı buffer id’ler üst üste); indeksli
tarafta dört benzersiz köşe ve index ile üçgenler paylaşılır. Aşağıda üçgenler
farklı renkle, köşeler renkli nokta, üstte ince kenar çizgisi ile bu fark
görselleştirilir; fare ile vertex / yüzey üzerinde buffer id okunur.
Sayfanın altındaki demo sabitleri tablosu,
initIndexedSplit içindeki rakamlarla birebir özetlenir.
Sol: 6 nokta; üst üste binen id’ler aynı köşede iki kez sayılır. Sağ: 4 nokta; köşe paylaşımı yüz hover metninde görülür. Üçgen renkleri (turuncu / camgöbeği) iki tarafta da aynı mantıkla eşleştirilir; sağda materyal grupları, solda vertex color ile ayrılır.
Demo sabitleri tablosu (İndeks split)
initIndexedSplit: iki BufferGeometry aynı kareyi çizer; sol
tarafta vertexColors, sağda setIndex + addGroup ile
iki MeshStandardMaterial.
| Sahne / rol | Parametre | Değer | Tür |
|---|---|---|---|
| Üçgen renkleri | TRI_A / TRI_B |
0xff7733 · 0x33ccee |
🔒 Sabit |
| İndekssiz | position |
Float32Array 6 köşe (iki üçgen, köşe kopyası) |
🔒 Sabit |
| İndekssiz | color |
18 float (köşe başına RGB); ilk üç köşe TRI_A, son üç
TRI_B
|
🔒 Sabit |
| İndeksli | position + setIndex |
4 köşe · indeks [0,1,2, 0,2,3] |
🔒 Sabit |
| İndeksli | addGroup |
(0, 3, 0) turuncu üçgen · (3, 3, 1) camgöbeği üçgen
|
🔒 Sabit |
| Üst üste id | NON_INDEXED_STACK_GROUPS |
[[0,3], [2,4]] (hover metni) |
🔒 Sabit |
| Raycaster | params.Points.threshold |
0.12 |
🔒 Sabit |
| Köşe noktaları | PointsMaterial |
size: 0.13 · vertexColors: true · HSL
0.05…0.8 aralığında id başına renk
|
🔒 Sabit |
| Kenar çizgisi | EdgesGeometry(geo, 18) |
LineBasicMaterial 0xf0f4ff · opaklık
0.55
|
🔒 Sabit |
| Mesh | MeshStandardMaterial |
sol: vertexColors: true · sağ: iki materyal
metalness 0.08 roughness 0.48
DoubleSide flatShading: true
|
🔒 Sabit |
| Kamera | PerspectiveCamera |
FOV 38 · yakın/uzak 0.1 / 50 · konum
(0, 0, 3.35)
|
🔒 Sabit |
| Işık | ortam + yönlü ×2 | Ambient(0xffffff, 0.42) · ana beyaz 1.0
(2.2, 3.8, 4.2) · dolgu 0xaaccff 0.28
(-3, -1, 2)
|
🔒 Sabit |
| Arka plan | scene.background |
0x05060c |
🔒 Sabit |
| Bellek özeti | ham position oranı |
6×3×4 B ÷ (4×3×4 B + 6×2 B indeks) ≈
1.20× (üst sınır ~2× metni HUD’da)
|
🔒 Hesap |
Önemli kod kesiti
İki geometrinin çekirdeği: solda aynı dünya köşesinin farklı buffer satırları, sağda paylaşılan köşe + indeks + grup.
const TRI_A = 0xff7733;
const TRI_B = 0x33ccee;
function buildNonIndexedColored() {
const g = new THREE.BufferGeometry();
const positions = new Float32Array([
-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0,
]);
g.setAttribute("position", new THREE.BufferAttribute(positions, 3));
// … her üç köşe için TRI_A / TRI_B RGB → color attribute
g.computeVertexNormals();
return g;
}
function buildIndexedGrouped() {
const g = new THREE.BufferGeometry();
const positions = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]);
g.setAttribute("position", new THREE.BufferAttribute(positions, 3));
g.setIndex([0, 1, 2, 0, 2, 3]);
g.clearGroups();
g.addGroup(0, 3, 0);
g.addGroup(3, 3, 1);
g.computeVertexNormals();
return g;
}
Teknik tablo: özel geometri akış şeması
| Adım | İşlem | Araç |
|---|---|---|
| 1. Veri hazırlığı | Koordinatları bir diziye yazın. | Float32Array |
| 2. Attribute atama | Veriyi GPU’nun okuyacağı biçime sokun. | THREE.BufferAttribute |
| 3. Geometriye bağlama | Özniteliği isimle kaydedin. | geometry.setAttribute('position', …) |
| 4. Normalleri hesaplama | Işıklandırma için yüzey yönlerini üretin (gerekirse). | geometry.computeVertexNormals() |
Uygulama: rastgele üçgen çorbası (wireframe)
Aşağıdaki örnek, çok sayıda rastgele üçgen üreterek “nokta bulutu” veya dağınık yüzey
hissi verir: her üçgen için üç vertex, her vertex için üç bileşen
(x, y, z) doldurulur; toplam uzunluk üçgen sayısı × 3 × 3.
const geometry = new THREE.BufferGeometry();
const triangleCount = 1000;
// Her üçgen: 3 vertex × 3 bileşen (x, y, z)
const positions = new Float32Array(triangleCount * 3 * 3);
for (let i = 0; i < positions.length; i++) {
positions[i] = (Math.random() - 0.5) * 10; // yaklaşık -5 … 5
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
Holodepth notu: dinamik güncelleme
(needsUpdate)
CPU’daki diziyi değiştirdikten sonra GPU’yu haberdar edin
Özel geometrinin köşelerini animasyon sırasında güncellemek istiyorsanız (örneğin
dalgalanan
bir yüzey), yalnızca Float32Array içindeki sayıları değiştirmek yetmez.
Verinin değiştiğini GPU’ya bildirmek için
geometry.attributes.position.needsUpdate = true; kullanın; böylece güncel
buffer ekran kartına yeniden aktarılır.