holodepth

3D Model Formatları · glTF / GLB · Geometri Verileri

Index Buffer glTF Yapısında Bağlantı Katmanı Nedir?

Vertex Data sayfası V0…V3 koordinatlarının dosyada nasıl yaşadığını öğretti: «köşeler nedir?» Bu sayfa bir sonraki soruya geçer: bu köşeler hangi sırayla okunup yüzey kurar?

Holodepth geometri hattında mesh iki katmandır: vertex listesi (konumlar ve attribute'lar) + index listesi (hangi vertex indekslerinin hangi üçgende birleşeceği). Index buffer, ikinci katmanın dosyadaki adıdır; bellek optimizasyonundan önce geometri yeniden kullanım sistemi olarak düşünülmelidir.

Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «POSITION bana noktaları verir; indices bana bu noktaları hangi sırayla üçgene bağlayacağımı söyler. Aynı vertex indeksi birden fazla üçgende tekrar kullanılabilir.»

Bu sayfanın sınırı · Vertex Data ile ayrım

Vertex Data (önceki sayfa) şunları anlatır; burada tekrarlanmaz:

  • Vertex / POSITION nedir, accessor zinciri, attribute semantic'leri
  • «Dört köşe = henüz yüzey yok» nokta bulutu kavramı
  • GPU'ya POSITION attribute yolculuğunun format birleşimi

Index Buffer (bu sayfa) şunları anlatır:

  • Index buffer nedir, indexed vs non-indexed mesh
  • Triangle assembly, winding order (CW / CCW)
  • Paylaşımlı vertex, glTF indices accessor'ı, componentType

Runtime API ve bellek profili için Three.js · Indexed vs Non-Indexed farklı bakış açısı sunar (WebGL buffer tipleri, Uint16Array); bu sayfa glTF dosya okumasına odaklıdır.

Index Buffer Nedir?

Çoğu eğitim index buffer'ı doğrudan «0, 1, 2» listesiyle tanıtır sanki mesh'in doğal ilk parçasıymış gibi. Holodepth'te sıra tersine işler: önce vertex listesinin ne taşımadığını netleştirirsiniz, sonra bağlantı tablosunun neden ayrı olduğunu görürsünüz. Bu bölümün giriş sorusu şudur: GPU aynı vertex'i neden tekrar tekrar saklamıyor?

Kısa cevap: aslında saklayabilir tek bir V0 kaydı vertex buffer'da bir kez durur. Sorun depolama değil, okuma tarifidir. İki farklı üçgen aynı köşeyi paylaştığında GPU'ya «Triangle A için şu satırı, Triangle B için yine aynı satırı çek» demenin yolu index buffer'dır. Koordinatı ikinci kez yazmazsınız; yalnızca aynı satır numarasına ikinci bir referans eklersiniz.

Vertex Data «köşeler nedir?» sorusunu cevapladı: V0…V3 koordinatları, POSITION accessor, nokta bulutu. Bu sayfa «bu köşeler hangi sırayla okunur?» sorusuna geçer. Koordinatları yeniden tanımlamıyoruz; önceki sayfadaki zemin dörtgenini bağlantı katmanı olmadan bıraktığımız yerden devam ediyoruz.

İki tablo: konum listesi ve bağlantı listesi

Indexed mesh okumak için zihinde iki ayrı liste tutulur. Birincisi vertex buffer (glTF'de attributes.POSITION ve paralel attribute'lar); ikincisi index buffer (glTF'de indices). İlki «adres defteri», ikincisi «üçgen tarifleri» gibidir tarifler adresi tekrar yazmaz, numaraya işaret eder.

Vertex Buffer (POSITION)          Index Buffer (indices)
─────────────────────────         ─────────────────────────
[0] V0  (-1, 0,  1)                0, 1, 2   → Triangle 0
[1] V1  ( 1, 0,  1)                0, 2, 3   → Triangle 1
[2] V2  ( 1, 0, -1)
[3] V3  (-1, 0, -1)

4 satır konum                       6 sayı = 2 üçgen × 3 referans

Tabloda satır numarası (0, 1, 2, 3) ile köşe etiketi (V0…V3) bilinçli olarak eşleştirilir: glTF ve GPU konuşmasında «vertex 2» derken kastedilen POSITION listesinin ikinci değil, sıfırdan sayılan 2. satırıdır yani bu örnekte V2. Index buffer'daki her değer bu tür bir satır numarasıdır; (x, y, z) float'ı taşımaz.

Index bir işaretçidir, kopya değil

Yeni başlayanların sık yaptığı karışıklık: index buffer'ı «ikinci bir vertex listesi» sanmak. Oysa ikinci listede uzay koordinatı yoktur; yalnızca birinci listeye geri dönen tam sayılar vardır. «0» demek «POSITION'ın 0. elemanını getir» demektir o elemandaki (x,y,z) zaten vertex buffer'da yazılıdır.

Vertex buffer cevaplar: «Köşeler nerede?» · Index buffer cevaplar: «Bu köşelerden hangi üçlü yüzey kurulsun, hangi sırayla?»

Aynı index değeri birden fazla üçgende tekrarlanabilir: yukarıdaki örnekte «0» hem Triangle 0'da hem Triangle 1'de geçer V0 iki yüzeyde ortaktır. Vertex buffer'da V0 hâlâ tek satırdır; index buffer iki ayrı yerde «0» yazar. Bu, Holodepth'in «geometri yeniden kullanım sistemi» tanımının çekirdeğidir; byte tasarrufu bu modelin sonucudur, tanımın kendisi değil.

GPU nasıl okur?

Draw çağrısı sırasında (bu sayfada API detayı yok) GPU tipik olarak index listesini sırayla üçlüler halinde gezer; her üçlü bir triangle assembly adımıdır (§3'te adlandırılır):

indices = [0, 1, 2,  0, 2, 3]

Üçlü 1: 0, 1, 2
  → POSITION[0], POSITION[1], POSITION[2]
  → uzayda V0, V1, V2 ile bir üçgen

Üçlü 2: 0, 2, 3
  → POSITION[0], POSITION[2], POSITION[3]
  → uzayda V0, V2, V3 ile ikinci üçgen (V0 ve V2 yeniden kullanıldı)

Dikkat: ikinci üçgende GPU yine POSITION[0] ve POSITION[2] satırlarını okur koordinatları vertex buffer'dan yeniden kopyalamaz, aynı satırlara tekrar referans verir. NORMAL, UV gibi diğer attribute'lar da aynı indeksle paralel okunur (semantic tanıtımı Vertex Data §3 ); bu sayfada odak yalnızca POSITION + index eşlemesidir.

Özet denklem: 4 vertex + 6 index = 2 triangle. Dört köşe dosyada bir kez yaşar; ekrandaki zemin dörtgeni, bu dört kayda altı referansla iki üçgen olarak kurulur.

Index olmadan aynı şekil nasıl tarif edilir?

glTF primitive'de indices alanı yoksa mesh non-indexed okunur: POSITION listesinin kendisi üçlüler halinde sıralanır, her ardışık üç satır bir üçgendir. Aynı zemin için V0, V1, V2 yazılır, ardından V0, V2, V3 tekrar yazılır ortak köşeler koordinat olarak kopyalanır. Görünen şekil aynı kalabilir; veri modeli farklıdır: bağlantı bilgisi vertex listesinin içine gömülür.

Indexed ve non-indexed yolların tablo karşılaştırması, küp örneği ve «kaç vertex taşınır?» sorusu §2'de genişletilir. Bu bölümde hatırlanacak tek cümle: index buffer, bağlantıyı vertex listesinden ayırarak aynı köşeye birden fazla üçgenin güvenle referans vermesini sağlar.

Vertex Tekrar Kullanım Problemi

§1'de index buffer'ın ne olduğunu «işaretçi» olarak tanımladık. Bu bölümün sorusu bir adım genişler: aynı geometrik köşe, veri modelinde neden birden fazla kez yazılsın ki? İki komşu üçgen ortak bir kenarı paylaştığında uzayda tek bir nokta vardır; dosyada bunu ya tek kayda çok referans (indexed) ya da aynı koordinatın kopyalanması (non-indexed) ile tarif edersiniz. İkisi de geçerli glTF yollarıdır; fark görünür şekilde değil, topolojinin nerede kodlandığında yatar.

Holodepth'te «vertex tekrar kullanım problemi» bir performans şikâyeti değil, okuma disiplini meselesidir: mesh'i okurken «kaç benzersiz köşe var?» ile «draw için kaç vertex satırı okunacak?» sorularını ayırmayı öğrenirsiniz. Indexed yolda bu iki sayı çoğu zaman farklıdır; non-indexed yolda ikinci sayı birinciden büyük veya eşit olur çünkü bağlantı, POSITION listesinin içine gömülür.

İki okuma modeli: indexed ve non-indexed

Indexed vs non-indexed format düzeyinde fark
Soru Indexed mesh Non-indexed mesh
glTF indices Var (accessor referansı) Yok
Bağlantı nerede? Ayrı index tablosunda POSITION sırasının kendisinde
POSITION count Benzersiz vertex sayısı Genelde üçgen sayısı × 3
Ortak köşe Tek satır + çok index referansı Aynı (x,y,z) birden fazla satırda

Vertex Data §4 non-indexed yolun varlığını kısaca işaret etmişti; burada iki model yan yana konur. Runtime bellek ve WebGL buffer tipleri Three.js · Indexed vs Non-Indexed sayfasının konusudur bu bölüm yalnızca glTF dosyasında «hangi tablo ne anlama gelir?» sorusuna odaklanır.

Dörtgen örneği aynı zemin, iki veri modeli

§1'deki zemin dörtgenini hatırlayın: dört benzersiz köşe, iki üçgen. Indexed tarif:

POSITION (4 satır)              indices (6 sayı)
V0, V1, V2, V3                  0, 1, 2,  0, 2, 3

Non-indexed tarif aynı ekran görüntüsünü üretir; fakat POSITION listesi bağlantıyı içerir ortak köşeler koordinat olarak tekrarlanır:

POSITION (6 satır  her üçlü bir üçgen)
satır 0–2:  V0, V1, V2     → Triangle 0
satır 3–5:  V0, V2, V3     → Triangle 1   (V0 ve V2 ikinci kez yazıldı)

indices yok
Zemin dörtgeni kayıt sayıları
Yol Benzersiz köşe POSITION satırı Index sayısı Üçgen
Indexed 4 4 6 2
Non-indexed 4 (uzayda) 6 2

Non-indexed okuma kuralı basittir: ardışık her üç POSITION satırı bir üçgendir. Index tablosu olmadığı için «hangi satırlar birleşir?» sorusunun cevabı listenin sırasına gömülüdür sırayı değiştirirseniz farklı üçgenler (veya bozuk geometri) elde edersiniz; indexed yolda bağlantı indices'te, konum POSITION'da ayrı düzenlenir.

Aynı şekil, farklı dosya hikâyesi

Viewer'da iki primitive yan yana aynı zemin gibi görünebilir. Format okuyucusu için kritik ayrım:

Geometrik eşitlik (ekranda aynı üçgenler) · veri eşitliği (dosyada aynı satır sayısı) aynı şey değildir.

Indexed modelde «4 vertex, 6 index» dersiniz; non-indexed modelde «6 vertex satırı, index yok» dersiniz. İkinci modelde V0 koordinatı iki satırda tekrarlanır uzayda aynı nokta, bellekte iki ayrı kayıt. Bu tekrar hata değil; non-indexed formatın bilinçli tasarımıdır. Bazı üretim hatları veya dönüştürücüler doğrudan üçlü POSITION listesi yazar; loader yine geçerli mesh üretir.

Küp örneği tekrar sayılabilir hale gelir

Zemin dörtgeninde tekrar iki köşeyle sınırlıydı; küpte paylaşım her yerde görünür. Bir küpün 8 benzersiz köşesi vardır; dış yüzeyi 12 üçgen (6 yüz × 2) ile kaplanır:

Indexed küp (tipik)
  8 POSITION satırı     → 8 benzersiz köşe koordinatı
  36 index              → 12 üçgen × 3 referans (köşeler tekrar referanslanır)

Non-indexed küp
  36 POSITION satırı    → her üçgende 3 koordinat sırayla; indices yok
  8 benzersiz köşe (uzayda) ama 36 satır (dosyada)

Örneğin bir köşe indexed modelde POSITION[3] olarak bir kez durur; o köşeye dokunan her yüzey üçgeni index buffer'da «3» yazar aynı satıra dört, beş veya daha fazla referans olabilir. Non-indexed modelde aynı (x,y,z) her yüzey üçgeni için ayrı satırda yeniden listelenir; uzayda bir köşe, dosyada çok satır.

Küp sayıları öğretim içindir; asıl mesele ölçektir: mesh büyüdükçe ortak köşe sayısı artar, indexed modelde POSITION count sabit kalırken index listesi üçgen sayısıyla büyür; non-indexed modelde POSITION count doğrudan üçgen sayısıyla orantılı artar. Bu fark dosya boyutuna yansır fakat Holodepth'te önce şu cümleyi kurun: indexed mesh, topolojiyi index tablosunda açık tutar; non-indexed mesh topolojiyi POSITION sırasına gömer.

Neden indexed yol varsayılan okuma?

glTF ekosisteminde çoğu üretilmiş mesh indexed gelir çünkü bağlantı katmanı ayrı okunabilir: debug sırasında «kaç benzersiz vertex?» sorusu POSITION count ile, «kaç üçgen?» sorusu indices count ÷ 3 ile yanıtlanır. Non-indexed primitive hâlâ geçerlidir; özellikle basit veya dönüştürülmemiş üçlü listelerde karşılaşılabilir.

Paylaşımlı köşenin görsel vurgusu ve «shared vertex count» düşüncesi §5'te derinleşir; triangle assembly adımları §3'te pipeline olarak adlandırılır. Bu bölümün çıkış cümlesi:

Vertex tekrar kullanım problemi = ortak köşeyi ya bir kez yazıp çok kez referanslamak (indexed), ya da her üçgende koordinatı yeniden yazmak (non-indexed). İkisi de aynı yüzeyi kurabilir; format okuyucusu hangi tablonun ne anlattığını ayırt edebilmelidir.

Triangle Assembly

Vertex Data §4 «nokta bulutu → üçgen ağı» geçişini kavramsal olarak tanıttı; §2 indexed / non-indexed veri modellerini yan yana koydu. Bu bölüm aradaki işlem adımını isimlendirir: dosyadaki index üçlüleri draw anında somut üçgen primitive listesine nasıl dönüşür? Holodepth'te bu adıma triangle assembly denir kullanıcı bu terimi burada ilk kez görür.

Assembly bir sıkıştırma tekniği değildir; okuma yorumudur. Vertex buffer ve index buffer dosyada durur; GPU (veya loader'ın hazırladığı draw komutu) index listesini sırayla üçlüler halinde gezer ve her üçlü için «şu üç vertex'i bir yüzey say» der. §1'deki «işaretçi» metaforunun draw tarafındaki karşılığı budur: index sayıları artık soyut satır numarası değil, çizilecek üçgenin köşe seçimidir.

Pipeline: assembly nerede durur?

glTF primitive (POSITION + indices + mode)
        ↓
Vertex Buffer     konum ve paralel attribute tabloları  (değişmez kaynak)
        ↓
Index Buffer      hangi vertex indeksleri, hangi sırayla   (üçlü tarifler)
        ↓
Triangle Assembly ← BU BÖLÜM: her 3 index → 1 üçgen primitive
        ↓
Rasterizer        üçgeni piksele çevirme                  (bu sayfada yok)

Üstteki şema format okuyucusu içindir shader, clipping ve fragment aşamaları bilinçli olarak kesilmiştir. Assembly ile rasterizer arasındaki sınır: assembly hâlâ «hangi üç köşe?» sorusundadır; rasterizer «ekranda hangi piksel?» sorusuna geçer. Vertex Data §4 «neden üçgen?» sorusunu cevapladı; bu bölüm «index üçlüsü o üçgeni nasıl seçer?» sorusunu cevaplar.

Pipeline aşamalarında «kim ne bilir?»
Aşama Girdi Çıktı (kavramsal)
Vertex buffer Accessor'dan çözülen float tablolar Vertex i → POSITION, NORMAL, UV, …
Index buffer UInt üçlü dizisi «Triangle k için indeksler: a, b, c»
Triangle assembly Index üçlüleri + vertex buffer Çizim birimi: 3 köşeli yüzey listesi
Rasterizer Üçgen köşeleri (clip uzayı) Fragment adayları

glTF mode — Assembly Kuralı

Primitive'deki mode alanı, index (veya non-indexed POSITION) dizisinin kaç elemanlı gruplar halinde yorumlanacağını söyler. glTF geometri sayfalarında varsayılan:

mode: 4   →  TRIANGLES   (her 3 index = 1 üçgen)
mode: 5   →  TRIANGLE_STRIP   (şerit; bu sayfada detay yok)
mode: 6   →  TRIANGLE_FAN     (fan; bu sayfada detay yok)

Çoğu üretilmiş mesh mode: 4 kullanır. Bu durumda indices accessor count değeri 3'ün katı olmalıdır; kat değilse primitive geçersiz veya export hatasıdır. Basit aritmetik:

üçgen sayısı = indices.count ÷ 3   (TRIANGLES modunda)

Non-indexed primitive'de assembly yine vardır; fark index tablosunun olmamasıdır POSITION listesi doğrudan üçlüler halinde okunur (§2). Assembly adımı «her ardışık üç satır bir üçgen» kuralıyla çalışır; indexed yolda üçlüler indices'ten gelir.

Adım adım assembly: zemin dörtgeni

§1 ve §2'deki indices = [0, 1, 2, 0, 2, 3] listesini assembly gözüyle okuyalım. Vertex buffer'da dört satır sabittir; assembly bu satırları yeniden sıralamaz ve kopyalamaz yalnızca hangi satırların hangi üçgende bir araya geleceğini seçer.

indices = [0, 1, 2,  0, 2, 3]

── Assembly adım 1 ──
  Okunan üçlü:     0, 1, 2
  Çekilen vertex:  POSITION[0], POSITION[1], POSITION[2]   → V0, V1, V2
  Üretilen primitive: Triangle 0

── Assembly adım 2 ──
  Okunan üçlü:     0, 2, 3
  Çekilen vertex:  POSITION[0], POSITION[2], POSITION[3]   → V0, V2, V3
  Üretilen primitive: Triangle 1   (V0 ve V2 ikinci kez referanslandı)
V0 ───────────────────────── V1
 │ ╲   T0                    │
 │   ╲                       │
 │     ╲  T1                 │
 V3 ───────────────────────── V2
      köşegen paylaşımı: V0 · V2

İki assembly adımının sonunda ekranda bir dörtgen yüzey vardır; vertex buffer hâlâ dört satırdır. «6 vertex taşındı» hissi yanıltıcıdır indexed yolda GPU altı index okur, dört benzersiz POSITION satırına referans verir. Bu ayrım §2'deki «benzersiz köşe vs draw okuması» tablosunun draw anındaki karşılığıdır.

Attribute'lar assembly ile birlikte çekilir

Assembly yalnızca POSITION'ı değil, aynı vertex indeksindeki tüm attribute'ları seçer. Index 2 dediğinizde loader tipik olarak:

POSITION[2]   → (x, y, z)
NORMAL[2]     → (nx, ny, nz)     (varsa)
TEXCOORD_0[2] → (u, v)           (varsa)

Semantic tanıtımı Vertex Data §3'te; burada kritik kural: assembly vertex buffer sırasını değiştirmez, satırları silmez, kopyalamaz. Yalnızca «bu üçlü draw için şu indeksler» der. NORMAL / UV hesabı veya eksik attribute debug'ı ilerideki geometri sayfalarına bırakılır.

Sıra burada anlamlıdır detay §4'te

Assembly adımında üçlünün yazılış sırası (0→1→2 mi, 0→2→1 mi) henüz yüzey yönünü belirler. Bu bölümde üçgenin oluştuğunu tarif ediyoruz; ön yüz / arka yüz ve CW / CCW ayrımı §4 · Winding Order'da merkeze alınır. Şimdilik yeterli cümle: assembly üç köşeyi bir araya getirir; sıra, o üçgenin hangi tarafının «ön» sayılacağını etkiler.

Vertex Field Lab ile aynı üçlüler

Metin ve 3D önizleme birbirini tamamlar: Vertex Field Lab · Quad Split modu, bu bölümdeki assembly adımlarının görsel karşılığıdır. Mod değiştirdiğinizde nokta bulutundan iki üçgene geçişi hissedersiniz; bu sayfa aynı geçişin dosya/index okuma dilimini adlandırır. Demo'yu tekrar anlatmıyoruz yalnızca «Triangle 0 / Triangle 1 = assembly çıktısı» bağlantısını kuruyoruz.

Triangle assembly = index buffer'daki her üçlünün, vertex buffer'dan üç köşe seçerek bir çizim primitive'i üretmesi. Dosyada üçgen diye ayrı bir dizi yoktur; üçgenler, indices'in TRİANGLES modunda üçer üçer okunmasıyla oluşur.

Winding Order

§3 triangle assembly ile index üçlüsünün üç köşeyi bir araya getirdiğini gördük. Bu bölümde üçüncü boyut eklenir: üçlünün yazılış sırası. Index buffer yalnızca «hangi köşeler» değil, hangi sırayla birleştirilecekleri sorusunun cevabıdır. Aynı üç vertex, aynı uzaysal konumda kalır; yalnızca index sırası değişirse tarif edilen yüzeyin önü ile arkası yer değiştirir.

Vertex Data §4 bu ayrımı bir notla işaret etmişti; winding order, Index Buffer sayfasında merkeze alınır. Holodepth format okuyucusu için kritik cümle: koordinatlar aynı kalabilir; index sırası değişince çizilen yüzey yönü değişir.

Aynı üçgen, iki yüzey yönü

Zemin örneğinde Triangle 0 köşeleri V0, V1, V2. Index buffer'da bu üçlü iki şekilde yazılabilir:

0, 1, 2     →  V0 → V1 → V2  (bir yönde dolaşım)
0, 2, 1     →  V0 → V2 → V1  (ters dolaşım)

Geometrik olarak aynı düzlemdeki aynı üç nokta; fakat GPU ve glTF viewer çoğu zaman yalnızca bir dolaşım yönünü ön yüz (front face) sayar, diğerini arka yüz (back face). Index buffer'da «1» ve «2»nin yer değiştirmesi koordinat taşımaz yalnızca assembly sırasını tersine çevirir.

Üstten bakış (yukarı = +y)

0, 1, 2  →  V0 ──────→ V1        Köşeleri bu sırayla birleştirince
              ╲                    bir dolaşım yönü (ör. CCW)
               ╲
                V2

0, 2, 1  →  V0 ──────→ V2        Aynı üçgen; köşe gezinti yönü ters
              ╱                    (ör. CW) → karşı yüz sayılır
             ╱
            V1

CW ve CCW saat yönü okuması

Winding order, üçgen düzlemine belirli bir taraftan bakıldığında köşeleri index sırasıyla birleştirirken izlediğiniz dönüş yönüdür:

  • CW (clockwise) — saat yönü
  • CCW (counter-clockwise) — saat yönü tersi

«Üstten bakış» ifadesi öğretim içindir; gerçek mesh'te kamera her yönden gelebilir. Önemli olan göreli kuraldır: pipeline bir yönü «ön» kabul eder glTF / WebGL ekosisteminde yaygın varsayılan, model uzayında CCW ön yüz kabulüdür (uygulama ve negatif ölçek durumlarında tersine dönebilir; bu sayfada matris detayı yok).

Index sırası ve yüzey yönü (özet)
Index üçlüsü Köşe dolaşımı Tipik yorum (CCW = ön varsayımıyla)
0, 1, 2 V0 → V1 → V2 Ön yüz (örnek zemin için)
0, 2, 1 V0 → V2 → V1 Arka yüz (aynı geometri)

Tablo tek üçgen içindir; mesh genelinde tüm üçgenlerin tutarlı aynı kurala uyması gerekir aksi halde bazı yüzeyler içe, bazıları dışa bakar («inside-out» veya delikli görünüm).

Backface Culling : Neden Sıra Önemli?

Render pipeline'da backface culling, arka yüz sayılan üçgenleri çizmeden atlar performans ve doğru görünürlük için. Culling açıkken yanlış winding, dosyada veri «doğru» görünse bile ekranda kaybolan yüzey üretir: üçgen vardır, assembly olur, fakat pipeline «bu arka yüz» deyip rasterize etmez.

Format okuyucusu için yeterli zincir:

Index üçlüsü sırası
    → winding (CW / CCW)
        → ön / arka yüz etiketi
            → culling kararı (çiz / atlama)

Shader kodu, depth test ve API bayrakları bu sayfanın dışındadır. Culling mantığının sahne tarafı Three.js · Bounding & Culling hattında genişler; burada yalnızca index sırası ile görünürlük arasındaki format köprüsü kurulur.

Non-indexed yolda da sıra vardır

Winding yalnızca index buffer'a özgü değildir. Non-indexed primitive'de POSITION listesindeki her ardışık üç satırın sırası aynı rolü oynar: satır 0→1→2 bir dolaşım, satırların fiziksel sırasını değiştirmek winding'i değiştirir. Fark, sıranın indices'te mi yoksa POSITION tekrarında mı kodlandığıdır (§2). Assembly §3'te her iki yolda da üçlü okunur; winding §4'te her iki yolda da üçlünün iç sırasına bağlıdır.

glTF: material.doubleSided ve export tutarlılığı

Primitive hangi malzemeyi kullanırsa, malzeme kaydındaki doubleSided alanı culling davranışını etkiler:

  • doubleSided: false (yaygın) yalnızca ön yüz güvenilir; winding hataları görünür kayıp yaratır
  • doubleSided: true her iki yüz de çizilebilir; winding hâlâ ışık / normal hesabını etkiler, yalnızca «tamamen yok olma» riski azalır

Export araçları (Blender, glTF exporter vb.) genelde mesh boyunca tutarlı bir CCW veya CW kuralı yazar. Dosyayı debug ederken primitive'i malzemeden koparmayın: «yüzey yok» şikâyeti bazen index byte'ından değil, doubleSided + winding kombinasyonundan kaynaklanır.

Sık görülen format okuma hataları

Winding kaynaklı sorunlar genelde şu üç senaryoda ortaya çıkar:

  • Ters export tüm index üçlüleri sistematik olarak CW/CCW ters; mesh «içeri bakıyor» gibi görünür
  • Köşegen yönü dörtgeni iki üçgene bölerken 0,1,2 + 0,2,3 yerine 0,1,3 + 1,2,3 gibi alternatif split; geometri doğru, winding tutarsız olabilir
  • Negatif ölçek node transform mirror içerir; runtime'da winding etkisi tersine döner (transform konusu sahne hattında)

Index buffer byte düzeni veya componentType bu belirtilerin yerine geçmez önce üçlü sırasını ve malzeme tarafını kontrol edin. Accessor detayı §6'da.

Winding order = index (veya non-indexed POSITION) üçlüsündeki sıranın, aynı köşeler için hangi yüzey yönünün «ön» sayılacağını belirlemesi. Index buffer hem köşe seçer hem dolaşım yönü yazar; assembly bu yönü somut üçgene taşır.

Shared Vertex Paylaşımlı Köşe

§1–§4 index buffer'ın ne olduğunu, iki veri modelini, assembly'yi ve winding'i tarif etti. Bu bölüm Holodepth'in ana tezine geri döner: indexed mesh bir geometri yeniden kullanım sistemidir. Özü şudur bir vertex kaydı, birden fazla üçgen tarafından referanslanabilir. Aynı POSITION satırına farklı index üçlüleri «dönüp bakar»; koordinat kopyalanmaz.

§2 bu fikri sayılarla karşılaştırmıştı (4 satır vs 6 satır). Burada kavramı kim kiminle paylaşılıyor? sorusuna indirgeriz. Paylaşımlı köşe (shared vertex), uzayda tek nokta + dosyada tek POSITION satırı + index buffer'da birden fazla referans üçgenidir.

Ne sayılır, ne sayılmaz?

Format okurken üç farklı «vertex sayısı» karıştırılır; shared vertex bölümünde ayrım şarttır:

  • POSITION count: Vertex buffer'daki benzersiz kayıt sayısı (paylaşımlı köşe dahil, her satır bir kez)
  • indices.count : Draw sırasında okunan index sayısı (§3: üçgen başına 3; paylaşım burada tekrarlanır)
  • Shared vertex kullanımı : Aynı vertex indeksinin birden fazla üçgende geçmesi (topoloji özeti; ayrı glTF alanı değil)

«Shared vertex count» tek bir JSON anahtarı değildir; mesh analizinde türetilen bir ölçüdür: örneğin indeks «2» kaç üçgende geçiyor? Aşağıdaki şema, zemin dörtgenindeki Triangle A ve B için hangi köşe etiketlerinin index üçlülerinde tekrarlandığını görsel özet olarak verir metin, şemadaki satırları tekrar etmez.

Indexed · V2 birden fazla üçgende
Triangle A
V0 V1 V2
Triangle B
V0 V2 V3

V2 vertex buffer'da tek satır; index buffer'da hem Triangle A hem B «2» indeksini kullanır → shared vertex count artar, vertex buffer satır sayısı artmaz.

Şemayı nasıl okumalı?

Yukarıdaki panel indexed yolu özetler: Triangle A ve B'nin köşe etiketleri listelenir; vurgulu harf aynı vertex buffer satırına işaret eden paylaşımlı referanstır. Şema indices = [0, 1, 2, 0, 2, 3] zincirinin insan okunur hâlidir assembly adımlarını §3 metinde, winding yönünü §4'te gördünüz; burada odak yalnızca ortak indeksin tekrar kullanımı.

Dikkat: zemin split'inde yalnızca vurgulu köşe değil, köşegen üzerindeki V0 da her iki üçgende ortaktır index buffer'da «0» iki kez geçer. Şema tek paylaşımı öne çıkarır; gerçek mesh'lerde bir vertex onlarca üçgende aynı indeksle dönebilir.

Ölçek: küp ve yoğun mesh

Dört köşeli zemin öğretim için küçük tutulur; paylaşım küpte belirginleşir: 8 POSITION satırı, 36 index; bir köşe indeksine komşu yüzey üçgenleri tekrar tekrar «aynı numaraya» yazar. Karakter veya çevre mesh'lerinde iç köşeler çok sayıda üçgende ortaktır indexed modelde POSITION count sabit kalırken index listesi büyür; bu, §2'deki «topoloji index'te açık» cümlesinin yoğun sahne karşılığıdır.

Non-indexed yolda aynı uzaysal köşe farklı üçgenler için koordinat olarak yeniden yazılır; paylaşım taklit edilir, referanslanmaz. Geometrik sonuç benzer görünebilir; veri modeli farklıdır shared vertex yalnızca indexed okumanın kavramıdır.

Paylaşımlı konum ≠ paylaşımlı normal

POSITION indeksi paylaşımı, NORMAL veya UV'nin de otomatik paylaşıldığı anlamına gelmez. Sert kenarlı küpte aynı (x,y,z) konumuna sahip iki vertex satırı bilinçli olarak tutulabilir yüzey normalleri farklı olduğu için «smooth» birleşim istenmeyen kenarda. Export araçları bu durumda vertex split yapar: uzayda aynı noktaya yakın iki kayıt, farklı NORMAL. Bu sayfa POSITION + index topolojisine odaklanır; normal / UV split ilerideki geometri sayfalarının konusudur.

Format debug ipucu: ekranda sert/yumuşak kenar beklediğiniz yerde sürpriz varsa önce winding ve malzeme, sonra aynı konumda duplicate POSITION satırı var mı? diye bakın paylaşım her zaman istenen davranış değildir.

Shared vertex = indexed mesh'te bir vertex indeksinin birden fazla üçgen tarafından referanslanması; vertex buffer'da tek satır, index buffer'da çoklu kullanım. Holodepth tanımı: geometri yeniden kullanımının dosyadaki somut imzası.

glTF Tarafı indices Accessor

§1–§5 kavramsal katmanda index buffer'ı tarif etti. Bu bölüm aynı zemin dörtgenini glTF JSON dilinde gösterir. Vertex Data §2'de attributes.POSITION: 0 yalnızca konum accessor'ına işaret ediyordu; indexed primitive'de ikinci bir referans gelir: indices: 1. Buradaki 1 bir köşe koordinatı değil accessors dizisindeki 1 numaralı kaydın indeksidir.

Örnek JSON — §1'deki zemin dörtgeni

Aşağıdaki parça öğretim amaçlıdır; tam dosya değil. Dört köşe (count: 4), altı index (count: 6), iki üçgen (§3) ve mode: 4 (TRIANGLES) ile önceki bölümlerdeki [0, 1, 2, 0, 2, 3] listesine karşılık gelir.

{
  "meshes": [{
    "primitives": [{
      "attributes": {
        "POSITION": 0
      },
      "indices": 1,
      "mode": 4
    }]
  }],
  "accessors": [
    {
      "bufferView": 0,
      "componentType": 5126,
      "count": 4,
      "type": "VEC3"
    },
    {
      "bufferView": 1,
      "componentType": 5123,
      "count": 6,
      "type": "SCALAR"
    }
  ]
}

Primitive üzerindeki üç alan

mesh.primitives[0] kaydı geometri okuması için giriş kapısıdır:

  • attributes.POSITION: 0 — «Konum verisi accessors[0] ile okunur» (Vertex Data ile aynı kural)
  • indices: 1 — «Üçgen bağlantısı accessors[1] ile okunur»; bu alan yoksa primitive non-indexed sayılır (§2)
  • mode: 4 — TRIANGLES; index listesi 3'ün katı uzunlukta yorumlanır (§3)

Primitive koordinat veya index byte'ı taşımaz; yalnızca hangi accessor? sorusunun cevabını tutar. Mesh görünür bir cisim bloğu değil; attribute ve indices referanslarının metadata'sıdır.

Accessor 0 — POSITION (vertex buffer)

accessors[0]
  bufferView: 0      → ham byte dilimi (buffers hattında)
  componentType: 5126 → FLOAT
  count: 4           → dört benzersiz vertex (V0…V3)
  type: VEC3         → her vertex (x, y, z)

Bu kayıt Vertex Data §1'deki dört köşe listesinin dosyadaki karşılığıdır. Örnekte min / max yazılmadı; gerçek export'larda POSITION accessor'ında sık görülür (bounding hesabı için). Index accessor'ında genelde bulunmaz.

Accessor 1 — indices (index buffer)

accessors[1]
  bufferView: 1      → ayrı byte dilimi (0 ile aynı .bin içinde olabilir)
  componentType: 5123 → UNSIGNED_SHORT (§7'de diğer tipler)
  count: 6           → altı index değeri = 2 üçgen × 3
  type: SCALAR        → her eleman tek tam sayı (vertex indeksi)

Binary tarafta bu altı skaler, örneğin 0, 1, 2, 0, 2, 3 uint16 dizisi olarak durur. Loader okurken her değer POSITION tablosuna indeks verir: «2» demek accessors[0]'ın ikinci VEC3 satırını çek demektir; §1'deki işaretçi kuralının dosya hâli.

İki accessor, iki soru

POSITION vs indices — aynı primitive içinde
Primitive alanı Accessor # type count (örnek) Cevapladığı soru
attributes.POSITION 0 VEC3 4 Köşeler nerede?
indices 1 SCALAR 6 Hangi köşeler hangi sırayla üçgen kurar?

Tablo, önceki bölümlerdeki blockquote ayrımının JSON karşılığıdır: vertex buffer sorusu · index buffer sorusu. componentType farkı (5126 float vs 5123 uint16) byte yorumunu belirler; ayrıntı §7'de.

Buffer zinciri — değişmeyen kural

Hem POSITION hem indices için ham veri yolu aynıdır:

buffers → bufferViews → accessors → primitive alanı
                         ↑
              POSITION: bufferView 0  ·  indices: bufferView 1

İki accessor farklı bufferView indeksleri kullanabilir; GLB içinde genelde aynı .bin chunk'ının bitişik veya ayrı dilimleridir. Kurallar Binary Buffers, BufferView ve Accessor Mantığı sayfalarında POSITION için anlatıldı; indices yalnızca farklı semantic + SCALAR tipi ekler.

indices yoksa ne olur?

Primitive'de indices anahtarı yoksa mesh non-indexed okunur; Vertex Data örneğindeki gibi yalnızca attributes.POSITION: 0 kalır. Bu durumda accessors[0].count genelde üçgen sayısı × 3'tür; bağlantı POSITION sırasına gömülüdür. Aynı dosyada bir primitive indexed, diğeri non-indexed olabilir; karıştırmayın.

glTF'de index buffer = primitive.indices → bir accessors kaydı (type: SCALAR) → bufferView → byte. Koordinat attributes içinde değil; bağlantı indices içinde ayrı referanslanır.

Index Tipi — componentType

§6 primitive'in indices: 1 ile bir accessor'a işaret ettiğini ve accessors[1]'in type: SCALAR olduğunu gösterdi. Bu kapanış bölümü son parçayı netleştirir: binary tarafta her index değeri kaç byte? Cevap componentType alanındadır. POSITION accessor'ı için 5126 (FLOAT) + VEC3 öğrenilmişti; indices için yalnızca tam sayı tipleri geçerlidir; koordinat float'ı değil, vertex satır numarası okunur.

Holodepth ayrımı: componentType «veri kalitesi» değil, byte okuma kuralıdır. Yanlış tip, Accessor Lens demosunda olduğu gibi aynı byte'ı yanlış yorumlatır; index tarafında sonuç genelde saçma üçgenler veya loader hatasıdır.

glTF sabitleri — indices için geçerli tipler

Indices accessor'ında yalnızca unsigned tam sayı componentType değerleri kullanılır. Her skaler bir vertex indeksidir; aralık, temsil edebileceği en büyük POSITION satır numarasını sınırlar:

Index componentType değerleri
Sabit Değer Byte Temsil edebileceği max indeks Tipik kullanım
UNSIGNED_BYTE 5121 1 255 ≤256 vertex; küçük prop, seyrek
UNSIGNED_SHORT 5123 2 65 535 Çoğu glTF mesh (§6 örneği)
UNSIGNED_INT 5125 4 4 294 967 295 65k+ benzersiz vertex

Seçim, POSITION count değerine bağlıdır: en büyük geçerli indeks count − 1'dir. Zemin örneğinde count: 4 → indeksler yalnızca 0…3 aralığında olmalıdır; UNSIGNED_BYTE fazlasıyla yeterlidir. Export aracı yine de sıklıkla UNSIGNED_SHORT yazar; uyumluluk ve araç varsayılanı nedeniyle, zorunluluk nedeniyle değil.

Doğrulama kuralı — indeks vs vertex sayısı

Yeni başlayanların sık karıştırdığı iki sayı:

  • indices.count — kaç index değeri okunur (ör. 6); üçgen sayısı × 3 (§3)
  • attributes.POSITION accessor count — kaç benzersiz vertex satırı var (ör. 4)

Index değerlerinin her biri ikinci sayıya bağlıdır:

0 ≤ index değeri < POSITION count (her skaler için)

Dört vertex varken index listesinde «6» görmek «altı vertex var» demek değildir; geçersiz satır numarasına işaret demektir. Sonuç: bozuk geometri, uçan üçgenler veya loader reddi. Bu, winding veya shared vertex hatasından farklı bir sınıftır; önce componentType + count çiftini doğrulayın.

Aynı üçlü, farklı byte düzeni

Kavramsal üçlü 0, 1, 2 her tipde aynı anlama gelir; binary düzen değişir:

Üç index: 0, 1, 2

UNSIGNED_BYTE   →  00 01 02           (3 byte)

UNSIGNED_SHORT  →  00 00  01 00  02 00   (6 byte, little-endian tipik)

UNSIGNED_INT    →  12 byte (her indeks 4 byte)

bufferView.byteLength, accessor.count ve componentType birlikte tutarlı olmalıdır: örneğin 6 × 2 byte = 12 byte SHORT listesi. Uyumsuzluk, POSITION tarafında olduğu gibi index tarafında da accessor zincirini kırar.

POSITION float'ı ile karıştırmayın

Aynı dosyada iki accessor yan yana durur; okuma kuralları farklıdır:

accessors[0]  componentType 5126  type VEC3    → float üçlü koordinat
accessors[1]  componentType 5123  type SCALAR  → uint16 vertex indeksi

Index buffer'a FLOAT uygulamak format ihlalidir. Format okuyucusu indices satırında «koordinat mı, satır numarası mı?» diye sorar; cevap her zaman satır numarasıdır (§1).

Runtime köprüsü — kısa atıf

Loader, componentType'a göre typed array üretir (Uint16Array, Uint32Array vb.). WebGL draw limitleri ve API tarafı Three.js · Indexed vs Non-Indexed · index tipi sayfasında anlatılır. Bu sayfa format hattında kalır: dosyada hangi sabit yazılı, POSITION count ile uyumlu mu?

Index Buffer sayfası — durma noktası

§1–§7 birlikte şu zinciri tamamlar: index buffer nedir → indexed / non-indexed → assembly → winding → shared vertex → glTF indices accessor → byte tipi. Shader, VAO binding ve buffer upload bu hattın dışındadır. Normal, UV ve diğer attribute'ların geometri hattındaki yeri bir sonraki sayfalara bırakılır.

Index Buffer format okuması: primitive.indicesSCALAR accessor → componentType ile uint dizisi → her değer POSITION tablosuna indeks → assembly ile üçgen. Bu cümleyi kurabiliyorsanız sayfa görevini tamamladınız.

Demo · Surface Compiler Lab

Vertex Field Lab Geometry Data hattını anlatır: koordinatlar, nokta, tek primitive. Bu laboratuvar ise Geometry Topology hattıdır; aynı dört köşe (glTF'de V0–V3) kullanılır ama soru değişir: hangi düğümler birbirine bağlı? Mesh burada çizilmez; derlenir. Index buffer bağlantı tablosudur; assembly bu tabloyu yüzeye çevirir.

Nasıl kullanılır? Sahnedeki düğümler (N0–N3) octahedron pin olarak durur; bakır renkli çizgiler connection stream'dir. Sol panelde Nodes / Primitives / Hubs sayacı topoloji metriklerini gösterir. Vertex Field'daki küre + cyan halo burada bilinçli olarak yok; farklı metafor, aynı matematik.

Beş derleme aşaması

  • Node Matrix — Dört düğüm hazır; POSITION lookup bekliyor, bağlantı yok (§1).
  • Connection Stream — Index akışı N0→N1→N2; bakır izler yanar, yüzey henüz dolu değil — yalnızca topoloji okunuyor (§3).
  • Topology Build — İlk primitive derlenir; CCW / CW ile yüz yönü seçilir (§4).
  • Shared References — İkinci bağlantı pass'i; N0 ve N2 hub olarak iki primitive'e referans verir (§5).
  • Compiled Surface — Tam çıktı tablosu [0,1,2,0,2,3] + UNSIGNED_SHORT (§7).
Demo · Mesh Compiler · TOPOLOGY

Node matrix · connection stream · compile pipeline · hubs

Derleme aşaması

Derleyici paneli · TOPOLOGY

Node registry · N0–N3 → idx

    Compile pipeline

    3D · topology compile

    Bu demo ne hissettirir? Vertex Field «nokta → üçgen» der; Surface Compiler «bağlantı → yüzey» der. Node Matrix'te koordinatlar vardır ama topoloji yoktur. Connection Stream'de bakır izler akar; henüz dolu mesh yok. Topology Build'de ilk yüzey derlenir. Shared References hub düğümlerini gösterir. Compiled Surface draw-ready çıktıdır. Demo bittiğinde şu ayrımı kurabilmelisiniz: «Vertex = veri; Index = bağlantı; Mesh = derlenmiş topoloji.»