Three.js · İleri geometri
Indexed vs Non-Indexed Geometry
WebGL ve Three.js’te bellek verimliliğinin matematiği
3D dünyasında bir objeyi ekrana çizmek, yalnızca noktaları (vertex) birleştirmekten çok daha fazlasıdır. Modern grafikte darboğaz çoğu zaman GPU belleği ve veri transferidir. Indexed (indisli) ile non-indexed (indissiz) geometri ayrımını bilmek, yalnızca kod yazmanızı değil — binlerce poligon altında o kodun nasıl ayakta kalacağını belirler.
Temel geometri haritası için Geometri Giriş; vertex katmanı için Attributes & buffer’lar; sahne bağlamı için Sahne (Scene).
Temel mantık: ham veri mi, akıllı referans mı?
Bir üçgen çizmek için üç nokta yeter. Peki bitişik iki üçgenden oluşan bir dörtgen (quad) düşünün: ortak kenarda aslında aynı uzaydaki iki köşe vardır.
Non-indexed (indissiz) yapı
Bu yöntemde her üçgen birbirinden bağımsızdır: “şu üç noktayı birleştir, sonra şu üç noktayı” dersiniz. Ortak köşeler pratikte aynı koordinatlara sahip olsa bile bellekte ayrı kayıtlar olarak durur — yani aynı veri çoğu zaman tekrarlanır. Model karmaşıklaştıkça (küre, karakter ağı vb.) bu tekrar katlanarak büyür.
Mini örnek: aynı köşe neden iki kez yazılır?
İki bitişik üçgeni (ortak kenarlı) non-indexed düşünün: her üçgen kendi üç “slot”unu sırayla listeler; ortak köşeler aynı harfle etiketlense bile bellekte ayrı kayıt olarak tekrarlanır.
Burada A ve C iki kez listelenir → vertex buffer’da iki kez yer kaplar.
Indexed düzende ise önce benzersiz köşeler (A, B, C, D) tek listede durur; üçgenler yalnızca indekslerle (ör. 0,1,2 ve 0,2,3) bu listeye işaret eder.
Indexed (indisli) yapı
Burada bir adres defteri mantığı vardır: önce benzersiz noktaları bir vertex dizisine yazarsınız; sonra “hangi üç nokta hangi üçgeni oluşturur?” sorusunu ayrı bir index dizisiyle yanıtlarsınız. Örneğin “0, 1, 2 ve ardından 2, 1, 3” gibi. Sonuç: pozisyon, normal, UV gibi attribute verisi mümkün olduğunca tek kez saklanır; üçgenler bu kayıtlara referans verir.
Index dizisinin tipi: Uint16Array mı Uint32Array mı?
Three.js / WebGL tarafında index verisi çoğu zaman Uint16Array veya
Uint32Array olarak tutulur. 16 bit indekslerle tek
draw’da adreslenebilen vertex indeks aralığı üst sınırı 65535 civarındadır
(uygulama ve draw moduna göre ayrıntı değişir); benzersiz vertex sayınız veya gereken
indeks aralığı bunu aşıyorsa Uint32Array gibi geniş tipe geçmek gerekir —
büyük sahnelerde bu ayrım “neden export bozuluyor?” sorusunun sık cevabıdır.
WebGL bağlamında bu indeks listesi, çizim komutlarında tipik olarak ELEMENT_ARRAY_BUFFER (öğe dizisi buffer’ı) olarak GPU belleğine bağlanır; vertex attribute’ları ise ARRAY_BUFFER tarafında durur — ileri seviye hata ayıklamada bu isimler dokümantasyonla hizalanır.
Canlı demo: aynı küp, indexed ↔
toNonIndexed()
Üstteki “8 köşe / 36 slot” sayımı kavramsal bir küp içindi; Three.js
BoxGeometry ise yüzey başına ayrı köşe tutarak (doku ve keskin yüzey için)
varsayılan olarak indexed bir buffer üretir. Aşağıda aynı kutuyu
geometry.toNonIndexed() ile indissiz hâle getirince
üçgen sayısı aynı kalırken position satırı uzar; tel kafes
yoğunluğu değişmez ama pembe vertex noktaları “patlama” gibi çoğalır —
veri gerçekten büyümüş olur.
Aşağıdaki demo sabitleri,
doc-indexed-nonindexed-demo.js içindeki
initIndexedNonindexedCubeDemo ile aynı değerleri listeler.
Özet
Mod değiştikçe metrikler güncellenir; Vertices sayısı geçişte
kısa bir animasyonla sayılır (ör. 24↔36) — “aha” anını güçlendirir.
Küp hafif asimetrik ve hafif eğik tutulur; böylece yüz normalleri ve UV
ayrımı daha okunur. Indexed modda pembe→turkuaz arası
nokta renkleri, aynı vertex indeksinin üçgenlerde kaç kez kullanıldığını
gösterir; sarı çizgiler “paylaşılan köşe” ipucu verir.
Non-indexed modda aynı uzay koordinatına düşen birden fazla
vertex kaydı hafifçe ayrıştırılır ve nabızla büyütülür — aynı köşede iki ayrı
veri satırı fikrini pekiştirir. Non-indexed + Keskin (flat)
birlikteyken sahnede daha sert specular (düşük roughness, daha güçlü
yönlü ışık) uygulanır. Yumuşak modda
material.flatShading = false ile fragment tarafında normal
interpolasyonu açılır; Normal okları her vertex kaydının
kendi normal vektörünü gösterir.
Öğretmen notu
Indexed vs non-indexed aslında bir render stili değil, buffer / örgü düzeni farkıdır; ekranda çoğu zaman ince farklar görünür ama veri yolu (vertex sayısı, indeks buffer’ı) net değişir.
Demo sabitleri tablosu (BoxGeometry · buffer)
Varsayılan BoxGeometry segmentleriyle bu örnekte indexed tarafta tipik olarak
24 köşe kaydı, 12 üçgen, 36 indeks öğesi
görünür; toNonIndexed() sonrası köşe satırı 36’ya çıkar,
indeks satırı yoktur (metrikte —).
| Sahne / rol | Parametre | Değer | Tür |
|---|---|---|---|
| Temel geometri | BoxGeometry |
(1.52, 1.2, 1.38) · varsayılan segment 1×1×1 |
🔒 Sabit |
| Non-indexed kopya | toNonIndexed() |
geoIndexed.clone().toNonIndexed() |
🔒 Sabit |
| Gövde materyali | MeshStandardMaterial |
renk 0x3d8fa9 · başlangıç flatShading: true ·
roughness/metalness moda göre (LIGHT)
|
🔒 + ↔ UI |
| Işık (genel) | HemisphereLight |
0xa7d8ff / 0x151820 · yoğunluk 1.2 |
🔒 Sabit |
| Işık (indexed / düz) | LIGHT |
ortam 0.42 · yönlü 2.4 ·
roughness 0.38 · metalness 0.22
|
🔒 Sabit |
| Işık (non-indexed + flat) | LIGHT |
ortam 0.26 · yönlü 3.05 ·
roughness 0.11 · metalness 0.06
|
🔒 Koşul |
| Yönlü konum | DirectionalLight.position |
(4.2, 6.5, 5) |
🔒 Sabit |
| Kamera | PerspectiveCamera |
FOV 42 · yakın/uzak 0.08 / 80 · konum
(2.85, 2.15, 3.55) · lookAt(0, 0.05, 0)
|
🔒 Sabit |
| Mesh dönüşü | mesh.rotation |
(0.2, -0.14, 0.11) |
🔒 Sabit |
| Tel kafes | WireframeGeometry + çizgi |
LineBasicMaterial 0x7ae8ff · opaklık
0.55
|
🔒 Sabit |
| Vertex noktası | SphereGeometry |
yarıçap 0.036 · segment 10×10 |
🔒 Sabit |
| Indexed renk eşlemesi | colorForIndexReuse |
tekrar 1 (veya diğer) → 0xff6a9a ·
2 → 0xffc14d · 3 →
0x44e8d8 · ≥4 → 0x5cf0ff
|
🔒 Sabit |
| Non-indexed çakışma | clusterCoincidentPositions |
epsScale = 5000 · halka yarıçap spread = 0.034 |
🔒 Sabit |
| Indexed “paylaşım” çizgisi | buildIndexedReuseLines |
yardımcı vektör (0.417, 0.709, -0.212) · ofset ölçeği
0.052 · çizgi 0xffd080 opaklık 0.42
|
🔒 Sabit |
| Vertex sayı animasyonu | VERT_TWEEN_MS |
560 · easeOutCubic |
🔒 Sabit |
| Normal okları | VertexNormalsHelper |
boy 0.16 · renk 0x7dffc4 |
↔ HTML |
| Dönen pivot | her kare | pivot.rotation.y += dt × 0.52 ·
pivot.rotation.x = sin(t×0.00035)×0.12
|
🔒 Sabit |
| Non-indexed nabız | tickNonIndexedDotPulse |
sin(now×0.0031) ile ölçek nabzı |
🔒 Sabit |
| Renderer | makeRenderer |
ACESFilmicToneMapping · exposure 1.45 ·
SRGBColorSpace · setPixelRatio(min(dpr,2)) ·
setSize(…, true)
|
🔒 Sabit |
| UI varsayılanları | segment / onay kutuları | Indexed · tel kafes açık · vertex noktaları açık · Keskin (flat) · normal okları kapalı | ↔ HTML |
Önemli kod kesiti
Aynı kutu iki geometri örneğine bağlanır; mod değişince mesh.geometry ve tel
kafes yenilenir.
const geoIndexed = new THREE.BoxGeometry(1.52, 1.2, 1.38);
const geoNonIndexed = geoIndexed.clone().toNonIndexed();
const LIGHT = {
ambIndexed: 0.42,
dirIndexed: 2.4,
ambNonFlat: 0.26,
dirNonFlat: 3.05,
roughIndexed: 0.38,
roughNonFlat: 0.11,
metalIndexed: 0.22,
metalNonFlat: 0.06,
};
function applyGeometry() {
const g = indexedMode ? geoIndexed : geoNonIndexed;
mesh.geometry = g;
disposeWireframe();
wire.geometry = new THREE.WireframeGeometry(g);
// … vertex noktaları, indeks-tekrar çizgileri, metrik animasyonu
applyLightingAndRoughness();
}
Derinlemesine: bellek, bant genişliği ve GPU önbelleği
Neden her zaman indexed kullanmıyoruz — ve bu ayrım neden kritik? Kısaca vertex reuse (nokta yeniden kullanımı) ve donanım davranışı.
Vertex reuse ve kabaca boyut
Tipik bir düzenlemede her vertex; örneğin pozisyon (3 float), normal (3 float) ve UV (2 float) taşıyorsa toplam 8 float ≈ 32 byte (Float32) mertebesindedir — kesin sayı kanal sayısına göre değişir.
Non-indexed örnek (kavramsal): bir küp için 12 üçgen vardır; her üçgen 3 vertex listesi gerektirir: 12 × 3 = 36 vertex slotu. Kabaca 36 × 32 byte ≈ 1152 byte yalnızca bu kanallar için (üst bağlam; gerçek primitive’de ek köşe/UV bölümleri sayıyı artırabilir).
Indexed örnek (kavramsal): aynı köşe verisi tek listede tutulur; üçgen bağlantısı çoğu zaman 16- veya 32-bit indekslerle tutulur. Toplam bellek genelde çok daha düşük kalır; karmaşık modellerde fark MB düzeyine çıkabilir.
GPU cache avantajı
Modern GPU’lar işlenen vertex’leri kısa süreli önbellekte tutar. Bir sonraki üçgen, az önce kullanılan bir indeksi tekrar çağırıyorsa, dönüşüm maliyeti düşebilir. Non-indexed düzende aynı köşe koordinatları bile ayrı slotlar olarak akabileceği için bu yeniden kullanım fırsatı zayıflar; veri “her seferinde yeni”ymiş gibi işlenebilir.
İleri seviye sezgi: Indexed çizimde aynı vertex indeksi kısa süre içinde yeniden kullanıldığında, GPU tarafındaki önbellek / post-transform mekanizmaları sayesinde vertex shader işi pratikte daha az tekrarlanabilir — bu, “draw call düştü” etkisinden ayrıca, yoğun geometride hissedilen bir rahatlamadır (her sürücü ve mesh düzeni için kesin sayı garantisi değildir).
Ne zaman hangisini seçmelisiniz?
| Özellik | Indexed geometry | Non-indexed geometry |
|---|---|---|
| Bellek kullanımı | Genelde çok verimli (tekrar az) | Yüksek (vertex verisi sık tekrarlanır) |
| Render / veri yolu | Cache ve reuse’a daha uygun | Büyük veride maliyet artabilir |
| Keskin kenarlar / yüz başına farklı normal | Zor (komşu yüzeyler vertex paylaşımı) | Kolay (yüzeyler bağımsız vertex ile ayrılabilir) |
| Tipik kullanım | Organik modeller, pürüzsüz yüzeyler | Patlama parçacıkları, keskin low-poly hatlar |
Kritik not: keskin yüzey ve farklı renk
Her yüzeyin görsel olarak tamamen bağımsız olması gerekiyorsa
(örneğin küpün her yüzüne farklı renk veya keskin gölgeler), indexed yapıda bir vertex
komşu tüm yüzeylerle aynı normali “paylaşmak” zorunda kalabileceği için kenarlar
yumuşak görünebilir. Bu durumda bazen
BufferGeometry.prototype.toNonIndexed() ile veriyi
expand etmek veya vertex’leri bilinçli olarak
klonlamak
gerekir — trade-off olarak bellek ve bant genişliği artar.
Holodepth uygulama stratejisi
Three.js’te birçok hazır BufferGeometry (ör. BoxGeometry,
SphereGeometry) varsayılan olarak indexed gelir. Özel
shader veya manuel vertex manipülasyonunda ise veriyi nasıl düzenlediğiniz, FPS’i doğrudan
etkiler.
- Verimlilik önceliği: Mümkün olduğunca index kullanın; gereksiz vertex kopyasından kaçının.
- Görsel keskinlik önceliği: Yumuşak normal paylaşımı istemiyorsanız non-indexed’e geçin veya köşeleri klonlayın; maliyeti göz önünde bulundurun.
Bir sonraki adımda bu yapı taşlarını kullanarak vertex verisini tek bir interleaved bellek düzeninde paketlemeyi Interleaved buffers üzerinden inceleyebilirsiniz.
İleri geometri dünyasına hoş geldiniz: burada yalnızca şekil çizmiyoruz — veriyi bilinçle yönetiyoruz.