holodepth

3D Model Formatları · glT F / GLB · Dosya Yapısı

Binary Bufferlar: Gerçek Veri Nerede Durur?

JSON Yapısı sayfasında glTF’nin organizasyon katmanını işledik: sahne nasıl parçalara ayrılır, hangi indeks hangi tabloya işaret eder. Bu sayfa anatominin ikinci yarısıdır ağır verinin nerede saklandığını ve JSON’un neden onu taşımadığını anlatır.

Accessor mantığı, bufferView dilimleme, vertex attribute düzeni, geometri sayfaları veya Draco sıkıştırması burada işlenmez; her biri ayrı konu olarak ileride açılır. Odak tek soruda: «glTF içindeki gerçek ham veri neden ve nasıl binary olarak saklanır?»

Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «JSON yalnızca bağlantıları ve organizasyonu saklar; asıl ağır veri binary buffer içinde durur buffer kördür, anlam sonradan gelir.»

Binary Buffer Nedir?

Binary buffer, glTF ekosisteminin mutlak ham veri deposudur. Sahneyi fiziksel olarak var eden; köşelerin uzaydaki kesin konumları (vertex positions), yüzeylerin ışığa tepkisini belirleyen normal vektörleri, dokuların yüzeye nasıl sarılacağını gösteren UV koordinatları, üçgenleştirme için gerekli indeks listeleri, animasyonlarda kullanılan anahtar kareler (keyframes) ve benzeri tüm ağır sayısal yükler, burada sıkıştırılmış, makine dostu bayt blokları halinde tutulur. JSON tarafı ise bu karmaşık yığının üzerine yerleşen akıllı bir navigasyon sistemi gibidir; JSON, buffer'ın içindeki o devasa sayı denizine dönüp «şu bayt aralığından şu kadarlık kısmı oku ve bunu bir vertex dizisi olarak yorumla» komutunu verir. Buffer, kendisini bir metin belgesi gibi açık etmez; o, saf ve yoğun bir binary (ikili) veridir.

Bu ayrımın neden hayati olduğunu kavramak için, çözüm üretmeye çalıştığı o devasa problemi görselleştirmek gerekir. Orta ölçekli, profesyonel bir 3D model dosyasında, her bir parçanın milyonlarca bileşen içerdiğini varsayalım:

  • 1.000.000 Vertex Konumu: Her biri 32-bitlik üç farklı float değerinden oluşan toplam 3 milyon kayan noktalı sayı.
  • 1.000.000 Normal Vektörü: Yüzey eğriliğini belirleyen yine 3 milyonluk bir sayı dizisi.
  • 1.000.000 UV Koordinatı: Doku haritalama için kullanılan 2 milyonluk bir sayı seti.

Eğer bu devasa veriyi bir JSON dosyasının içine gömerek, metin tabanlı (text-based) bir formatta taşımaya kalkarsanız, karşı karşıya kalacağınız tablo şöyledir:

[
  0.14500000298023224, 2.5330000054321305, 1.9210000034323211,
  -0.882000004323211, 0.004000001232321, 3.110000021323210,
  ...
]

Böyle bir yaklaşımın sonucu kaçınılmaz bir felakettir: Devasa dosya boyutları, milyonlarca ondalıklı sayıyı metinden sayıya dönüştürmek için gereken işlemci (CPU) maliyeti ve bu devasa metin dizisini belleğe yüklerken oluşan gereksiz RAM tüketimi. JSON formatı, insanların kolayca okuyabileceği ve sistemlerin esnekçe yönetebileceği bir "organizasyon dili" için tasarlanmıştır; milyonlarca hassas ondalıklı sayıyı satır satır taşımak için değil.

İşte glTF, bu yüzden akıllıca bir hibrit yöntem olan iki katmanlı bir yapı kullanır: Üstte mimariyi kuran, referansları düzenleyen ve meta verileri barındıran JSON; altta ise donanımın doğrudan içine akabileceği, yoğun, hızlı ve ham makine verisinden oluşan binary buffer. İkisi birlikte tek bir sahne dosyasının ruhunu oluşturur; JSON, sahnenin rotasını çizen o eşsiz "harita"dır; binary buffer ise arazinin ta kendisidir.

Neden Ayrı Bir Buffer Katmanı Var?

glTF’de buffer katmanı «sonradan eklenmiş bir optimizasyon» değildir; formatın temel mimari kararıdır. Bir 3B sahne dosyası iki tür bilgi taşır: ilişkiler (hangi parça nereye bağlı?) ve sayılar (o parçanın geometrik içeriği nedir?). Bu iki tür bilgi doğası gereği farklı işlenir, farklı araçlarla okunur ve farklı bellek biçimlerinde yaşar. glTF bunları tek bir katmanda birleştirmek yerine bilinçli olarak ayırır.

Kısa formül: JSON organizasyonu tarif eder, buffer ham veriyi taşır. JSON’da «bu mesh şu materyali kullanır», «bu düğüm şu çocuğa bağlıdır» gibi grafik ilişkileri okunabilir biçimde durur; fakat «vertex #482.731’in X değeri 1,847» gibi satırlar JSON’a yazılmaz çünkü bu ikinci grup bilgi yoğun, tekrarlı ve sayısaldır. İlişkiler seyrek ve anlamlıdır; vertex verisi ise sürekli ve hacimlidir.

İki farklı soru, iki farklı katman

JSON yapısı sayfasında mimari düzeyde bahsettiğimiz o kritik «organizasyon ve ham veri» ayrımı, sadece bir tasarım tercihi değil, WebGL ve modern 3D grafik işleme süreçlerinin bir varoluş zorunluluğudur. Bu sayfa, sistemin yüzeyindeki haritadan bir adım daha derine inerek, bu felsefi ayrımın neden saf ve yoğun bir buffer (tampon bellek) katmanı gerektirdiğini mimari bir dille derinleştirir. Bir 3D varlık (asset) bilgisayarın belleğine yüklenirken, grafik motorunun o modele dair sormak zorunda olduğu, doğası gereği birbirinden tamamen farklı iki temel soru kümesi vardır:

  • Organizasyon (Mimari) Soruları: Sahnenin ana kökü uzaysal olarak hangi düğümleri (node) kapsıyor? Bu çizim çağrısındaki (draw call) temel geometri (primitive), fiziksel olarak hangi materyal indeksine sıkı sıkıya bağlı? Kompleks bir animasyon iskeleti (rigging), tam olarak hangi düğümün rotasyon veya pozisyon özelliklerini (properties) hedef alarak manipüle ediyor?
  • Ham Veri (Payload) Soruları: Bu devasa mesh yapısının milyonlarca köşe (vertex) koordinatı bellekte peş peşe dizilmiş hangi ham sayılardan oluşuyor? Grafik kartının üçgenleri çizmek için ihtiyaç duyduğu topolojik indeks dizisi tam olarak nedir? Yüzeye doku sarmak için kullanılan UV kanalları, GPU'ya aktarılmayı bekleyen hangi devasa 32-bit float (kayan noktalı sayı) bloklarını içeriyor?

İşte bu temel mimari ayrımda; ilk gruptaki o yapısal ve ilişkisel soruların yanıtı her zaman JSON haritasında yatar; donanımı asıl terletecek olan o yoğun, dizilmiş sayılar yığınının (ikinci grup) yanıtı ise doğrudan "Buffer" katmanında aranır. Eğer bu iki farklı bilgi katmanı birbirine karıştırılırsa, sistemin donanımsal dengesi bozulur. Milyonlarca noktalı ham veriyi JSON'un içine düz metin olarak tıkıştırmaya kalkarsanız, dosya anında devasa boyutlara ulaşır, ayrıştırma (parsing) süresi çöker ve tarayıcının ana işlemci iş parçacığı (main thread) kilitlenir (1. bölüm bu darboğazı açıklar). Öte yandan, organizasyonel hiyerarşiyi binary'nin o karanlık yapısına gömerseniz, dosya tamamen kapalı bir kara kutuya dönüşür, insanlar veya hata ayıklama (debug) araçları tarafından okunamaz hale gelir. Kısacası Buffer katmanı, işlenmemiş, saf matematiğin ve o devasa veri okyanusunun (ikinci soru grubunun) fiziksel ev adresidir; JSON ise o okyanusta motorun yön bulmasını sağlayan akıllı, yapısal haritanın ta kendisidir.

Ayrımın Tasarım Kazanımları

Bu mimari katmanlaşma stratejisi, sadece dosya boyutunu optimize etmekle kalmaz; aynı zamanda sistemin her iki tarafındaki sorumluluk alanlarını (separation of concerns) keskin çizgilerle netleştirir. Her bir katman kendi uzmanlık alanında tek bir işi kusursuz yapmaya odaklanır; bu da hem teknik spesifikasyonun okunabilirliğini hem de bu şemayı yorumlayacak loader (yükleyici) yazılımlarının mantıksal tutarlılığını tahmin edilebilir ve sürdürülebilir kılar:

  • Bağımsız Evrim (Decoupled Evolution): JSON tarafındaki organizasyon katmanı ile Buffer tarafındaki ham veri katmanı birbirinden bağımsız yaşayabilir. Örneğin, bir modelin materyal referanslarını veya düğüm hiyerarşisini güncellemek, geometrik vertex verilerine dokunmayı gerektirmez. Organizasyon katmanı üzerinde versiyonlama, diff karşılaştırmaları, merge işlemleri veya validasyon kuralları uygulamak son derece hafiftir; oysa aynı işlemleri milyonlarca float (kayan noktalı sayı) üzerinde yapmaya çalışmak, hem gereksiz bir bellek tüketimidir hem de pratik bir karşılığı yoktur.
  • Binary Tarafının Araçlarla Uyumu (Tooling Alignment): 3D grafik dünyasının endüstriyel standartları (DCC araçları, vertex optimizasyon algoritmaları, GPU sürücüleri), sayısal veriyi işlemeye zaten hazırdır. Bu araçlar, devasa mesh verisini doğrudan ham bayt blokları olarak üretir ve tüketir. Bu yüksek performanslı veriyi tekrar JSON metin formatına serileştirmek, sadece CPU'yu gereksiz yere meşgul eden yavaşlatıcı bir "tercüme" adımı olurdu.
  • İki Farklı Hız Profili: Tarayıcı, dosyanın organizasyon katmanını JSON.parse gibi yerleşik ve optimize metotlarla saniyeler içinde ayağa kaldırarak sahnenin iskeletini oluşturur. Ağır binary veri blokları ise bu iskelet ayağa kalktıktan sonra, ayrı bir veri akışı (chunk) veya dosya olarak, genelde tek seferde ve sıralı biçimde okunur. İlişki grafiği (JSON) ile sayısal yük (Buffer) aynı anda ve aynı yöntemle parse edilmek zorunda değildir; bu da "zamanında yükleme" (lazy loading) stratejileri için biçilmiş kaftandır.
  • Genişletilebilirlik ve İhtisaslaşma: JSON tarafında extensions alanı üzerinden yepyeni organizasyon yetenekleri (örneğin daha karmaşık ışık modelleri) eklenirken, binary tarafında farklı sıkıştırma algoritmaları veya alternatif veri kodlama yöntemleri (Draco, Meshopt vb.) buffer içeriğini dönüştürür. Burada hayati olan nokta, organizasyonel yeniliklerin (JSON), ham verinin (Binary) kodlanma biçimiyle çakışmadan kendi kanalından özgürce genişleyebilmesidir. Gelecekte çıkacak her türlü yeni sıkıştırma teknolojisi, "ham verinin zaten binary kanalda olması gerektiğini" bildiği için, sistemin genel mimarisini bozmadan bu kanala entegre olur.

Katman Yığını: Her Halka Tek Bir Görev

Bir 3D dosyası içerisinde buffer (tampon bellek) kendi başına "şu köşe koordinatlarını (vertex) oku" gibi karmaşık bir emir veremez. Ham verinin saf olduğu bu noktada, JSON organizasyon katmanı ile Buffer veri yığını arasında, veriyi anlamlı bir 3D geometrisine dönüştüren hayati çeviri katmanları bulunur. Bu katmanların detaylı mekaniği, accessor ve bufferView sayfalarında tüm çıplaklığıyla işlenmektedir; burada ise yalnızca bu kadar katmana neden ihtiyaç duyduğumuzun mimari gerekçesi özetlenir. Her bir katman, verinin yolculuğunda tek bir boşluğu doldurarak sistemi hata payı sıfıra yakın, deterministik bir yapıya kavuşturur:

  • JSON Katmanı (Mantıksal İhtiyaç): «Bu 3D nesnenin (primitive) yüzey dokusunu oluşturmak için şu pozisyon verisine ihtiyacım var ve o veriyi bulabileceğin accessor indeksi şudur» diyerek, sahnenin hangi veriye ihtiyaç duyduğunu mantıksal düzeyde yanıtlar.
  • Accessor / BufferView Katmanı (Yorumlama Katmanı): «Talep edilen o veri, ana buffer bloğu içerisinde şu bayt başlangıcından itibaren şu kadar uzunlukta, şu tipte (örneğin float32 veya unsigned short) duruyor» diyerek, saf ve sessiz baytları nasıl yorumlamanız gerektiği sorusunu yanıtlar. Bu katman, verinin dil bilgisidir; buffer'ın tek başına bir anlamı olmadığını, ancak bu üst katmanların sağladığı "yönetici etiketleri" ile 3D objeye dönüştüğünü unutmayın.
  • Buffer Katmanı (Depo): «İşte ham verinin ta kendisi; burada hiçbir etiket, hiçbir anlam veya hiyerarşi yok, sadece sayılar var» diyerek, tüm sayısal değerlerin fiziksel olarak depolandığı o devasa bayt havuzunu sunar.

Dosya içerisindeki veri akışı, zihinsel olarak her zaman yukarıdan aşağıya doğru bir "anlamlandırma hiyerarşisi" şeklinde düşünülmelidir; okuma yönü organizasyondan başlar, verinin en saf haline, yani dosyanın en altında yatan en temel sayısal değerlere kadar uzanır:

Katman JSON → buffer veri hattı (özet)
JSON organizasyon
Accessor · sonraki sayfa
BufferView · sonraki sayfa
Buffer ham bayt havuzu

Pratikte ne görürsünüz?

Bir .gltf dosyasını metin editöründe açtığınızda nodes, meshes, materials gibi tabloları okursunuz sahne planını görürsünüz. Aynı modelin .bin dosyasını (veya .glb içindeki BIN chunk’ı) açtığınızda ise anlamlı metin görmezsiniz; yalnızca bayt akışı vardır. Bu iki dosya yan yana durduğunda «neden ayrı katman?» sorusunun cevabı somutlaşır: biri insan ve araçlar için harita, diğeri makine için yük.

JSON tarafında buffer’a giden yol genelde bir indeks zinciriyle başlar örneğin bir primitive, position verisi için bir accessor numarası taşır; fakat o numaranın buffer içindeki baytları nasıl çözeceği bu sayfanın kapsamı dışındadır. Burada sabitlenmesi gereken tek gerçek şudur: JSON doğrudan vertex taşımaz; buffer taşır. Üst katmanlar aradaki köprüdür; köprünün mühendisliği sonraki sayfalara bırakılır.

Özet cümle: glTF, 3B varlığı «ilişki belgesi + sayısal depo» olarak ikiye böler. Buffer katmanı bu depodur; JSON ise depodaki rafların hangi mesh’e, hangi animasyona ait olduğunu tarif eden organizasyon sistemidir. Bu ayrımı kavradığınızda 3. bölüm (liman analojisi) ve ardından buffer’ın içeriği ile havuz yapısı doğal olarak yerine oturur.

Konteyner Limanı Analojisi

Havuz metaforu buffer’ın şeklini anlatır; liman analojisi ise JSON ile buffer arasındaki iş bölümünü günlük hayattan hatırlatır. Büyük bir konteyner limanında iki tür bilgi vardır: liman planı (hangi konteyner nereye gidecek?) ve konteynerin içindeki gerçek yük (makine parçaları, tekstil, elektronik…).

glTF’de bu ayrım bire bir oturur:

  • JSON = liman planı okunabilir talimatlar, rota, bağlantılar
  • Buffer = konteynerlerin kendisi içinde taşınan ham yük

JSON şunu söyler: «Konteyner 42, Depo B’ye gidecek; içindeki yük mesh #3’ün position verisidir.» Plan, yükün ağırlığını veya boyutunu taşımak zorunda değildir; yalnızca ilişkiyi tarif eder. Buffer ise konteynerin içini dolduran malzemedir dışarıdan bakıldığında «konteyner #42» görürsünüz; içinde position mı UV mi olduğu kapaktan okunmaz.

Liman planı JSON’da kalır

Liman müdürü (loader veya siz, geliştirici) önce plana bakar: hangi konteyner hangi rafa, hangi gemiye, hangi kamyona? glTF JSON’u aynı rolü üstlenir «bu primitive şu accessor’a, bu accessor şu bufferView’a, bu bufferView şu buffer’a» der. Plan metindir; diff’lenebilir, insan gözüyle okunabilir, validasyon araçlarıyla kontrol edilebilir.

Planda «konteyner 42’nin içinde 0,123 float değeri var» yazmazsınız; «konteyner 42, segment A’da duruyor» yazarsınız. glTF’de de JSON’da vertex listesi değil, segment referansları vardır.

Yük buffer’da kalır

Konteynerin içi gerçek yük buffer’dır. Position koordinatları, normal vektörleri, indeks listeleri kapalı kapak arkasında, etiketsiz bayt olarak taşınır. Liman işçisi (CPU) konteyneri açtığında yine önce dış etiket okumaz; plan kağıdından «şu aralık position» bilgisini alır. Buffer’ın kendisi «ben position taşıyorum» demez.

Aynı limanda onlarca konteyner yan yana durabilir; tek bir büyük buffer şeridinde position, normal, UV ve indeks segmentleri peş peşe dizilir 5. bölümdeki havuz fikrinin liman karşılığı budur.

Accessor ve BufferView = etiket makinesi

Liman analojisinde bir sonraki adım şudur: konteyner kapalıyken içeriği nasıl tanırsınız? Cevap: plan + etiket sistemi. glTF’de bu rol bufferView («konteynerin şu bölümü») ve accessor («içerik tipi: float × 3, stride şu») ile doldurulur. Bu sayfada yalnızca şunu sabitleyin: buffer konteynerdir; anlam plan ve etiketlerle gelir. Sonraki iki sayfa, etiket sisteminin mühendisliğidir liman planını okuduktan sonra konteyneri açma rehberi.

Buffer İçerisinde Neler Bulunabilir?

Buffer tek bir «vertex listesi» değildir; glTF’nin ihtiyaç duyduğu her türlü ham geometri ve animasyon bilgisi bu havuzda yer alabilir. Örnek veri türleri (nasıl yorumlandıkları bu sayfada anlatılmaz yalnızca ne tür içerik taşınabileceği listelenir):

  • Vertex positions (köşe konumları)
  • Normals (yüzey yönleri)
  • Tangents (normal haritalama için)
  • UV coordinates (doku koordinatları)
  • Vertex colors (köşe renkleri)
  • Joint data ve weights (iskelet animasyonu için)
  • Indices (üçgen indeksleri)
  • Morph target verileri (blend shape)
  • Animasyon anahtar kareleri (time / value dizileri)

Ortak fikir şudur: buffer, anlam taşıyan alan adlarıyla değil, ardışık baytlarla doludur. «Position», «normal» veya «UV» etiketleri JSON ve üst katmanlarda verilir; buffer içinde yalnızca sıfır ve birlerin dizisi görülür. Körlük fikri 6. bölümde başlı başına işlenir.

Buffer Bir Dosya Değil, Veri Havuzudur

Önceki bölüm buffer’da hangi tür verilerin bulunabileceğini listeledi. Bu bölüm ise buffer’ın yapısal kimliğini sabitler: glTF’de buffer, anlamlı alan adlarıyla bölünmüş bir JSON dizisi değil; sürekli, etiketsiz bir bayt havuzudur. Yeni başlayanların sık yaptığı hata tam burada başlar: «Buffer = vertex listesi» veya «.bin dosyası = position array» sanmak.

Oysa tek bir buffer, birden fazla mantıksal veri bloğunu yan yana barındırabilir tıpkı RAM’deki bir bellek şeridinde farklı segmentlerin ardışık dizilmesi gibi. Position bloğu biter, normal bloğu başlar; ardından UV, indeks ve animasyon verisi gelir. Buffer bu segmentlerin hepsini tek şeritte taşıyan depodur; hangi bayt aralığının ne anlama geldiği havuzun içinde yazılı değildir.

Liste değil, sürekli şerit

Üç.js veya benzeri API’lerde geometri çoğu zaman «position array + normal array + index array» şeklinde ayrı diziler olarak düşünülür. Bu runtime modeli anlaşılırdır; fakat glTF dosyasındaki buffer böyle görünmez. Dosya seviyesinde karşılaştığınız şey, çoğu zaman tek bir byteLength uzunluğunda bloktur içinde birden fazla mantıksal dizi yan yana paketlenmiştir.

Şöyle düşünün: vertex listesi metaforu «her satırda bir köşe» okumayı kolaylaştırır; havuz metaforu ise «uzun bir bantta peş peşe baytlar» okumayı zorunlu kılar. İkinci model glTF’nin gerçeğine daha yakındır. Loader veya DCC export aracı bu bandı okurken, hangi ofsetten hangi tipe geçileceğini üst katmanlardan öğrenir buffer kendi başına «şuradan normal başlıyor» demez.

Havuz ≠ dosya adı

Başlıktaki «dosya değil» ifadesi şunu vurgular: buffer kavramı, model.bin uzantısıyla bire bir örtüşmez. JSON’daki buffers tablosu, mantıksal bir veri havuzunu tanımlar; bu havuz fiziksel olarak:

  • ayrı bir .bin dosyasında,
  • .glb içindeki BIN chunk’ta (11. bölüm),
  • veya (daha seyrek) uzak bir URI üzerinden

bulunabilir. Yani «buffer açtım» dediğinizde çoğu zaman aslında fiziksel konteyneri açmış olursunuz; içindeki mantıksal havuz ise o konteynerin tamamı veya bir parçası olabilir. Bir glTF modelinde birden fazla buffer girişi de olabilir örneğin geometri bir havuzda, gömülü küçük bir görsel başka bir havuzda. Ortak nokta şudur: her giriş etiketsiz bayt deposu olarak tanımlanır; içerik türü dosya adından veya uzantıdan çıkarılmaz.

Tek havuz, birden fazla tüketici

Havuz metaforunun ikinci gücü paylaşımdır. Aynı buffer şeridi, farklı mesh’lerin farklı primitive’leri tarafından referanslanabilir; bir modeldeki tüm geometri tek bir büyük havuza paketlenmiş olabilir. Export araçları bunu sıkça yapar: vertex verilerini ardışık yazar, indeksleri ayrı bir segmente koyar, animasyon kanallarını sona ekler hepsi aynı byteLength altında.

Bu, buffer’ın «tek bir mesh’e ait vertex listesi» olmadığını gösterir. Buffer, sahnenin veya dosyanın ortak ham bellek alanıdır; hangi mesh’in hangi segmenti kullandığı JSON organizasyonunda ve sonraki katmanlarda çözülür. Runtime’da motor, aynı ArrayBuffer üzerinden farklı typed array görünümleri oluşturabilir yine havuz tek, bakış açıları çoktur. Attribute layout ve GPU tarafı bu sayfanın dışındadır; burada yalnızca dosyada verinin tek şerit halinde toplandığı fikri önemlidir.

Segment sınırlarının nasıl tanımlandığı bufferView konusudur. Bu sayfada bilmeniz gereken tek şey: bufferView, havuzdaki «şu bayttan şu bayta kadar» dilimleri tarif eder; buffer ise dilimlerin tamamının üzerinde durduğu zemindir.

Havuz Tek buffer içindeki mantıksal segmentler
Buffer · bayt havuzu
  1. positions köşe koordinatları (ham float baytları)
  2. normals yüzey yönleri
  3. uvs doku koordinatları
  4. indices üçgen indeks listesi
  5. animation anahtar kare sayısal verileri

Araçlarda gördüğünüz şey çoğu zaman yorumdur

glTF görüntüleyiciler, Blender import pencereleri veya tarayıcıdaki mesh inspector’ları size «position: 24 vertex», «indices: 36 eleman» gibi okunabilir özetler sunar. Bu faydalıdır; fakat «buffer’ı açtım, vertexleri gördüm» cümlesi bu bağlamda genelde eksik veya yanıltıcıdır. Gördüğünüz şey, havuzun accessor/bufferView kurallarıyla yorumlanmış çıktısıdır tıpkı hex editördeki ham baytları bir struct şemasıyla okumak gibi.

Ham .bin veya BIN chunk’ta ise «vertex #3» diye bir satır yoktur; yalnızca ardışık baytlar vardır. Bu yüzden glTF anatomisini öğrenirken zihinsel modeli şöyle güncellemek işe yarar:

  • Buffer = depo / şerit / havuz (içerik türü yazısız)
  • JSON + üst katmanlar = rafların etiketleri ve «hangi raftan ne alınır» talimatı
  • Runtime geometri = talimatların uygulanmış hali (ayrı konular)

Yalnızca boyut bilgisi

JSON’daki bir buffer girişi çoğu zaman yalnızca havuzun toplam uzunluğunu söyler örneğin byteLength: 576. Bu sayı «576 vertex var» anlamına gelmez; «576 baytlık sürekli bir blok var» der. O blok içinde kaç float, kaç indeks veya kaç animasyon örneği olduğu buffer kaydının kendisinden çıkarılamaz; segmentlere ve yorumlama kurallarına ihtiyaç vardır.

Özet cümle: glTF buffer’ı bir dosya türü veya vertex listesi olarak değil, etiketsiz bayt havuzu olarak düşünün. Tek şerit, çok segment, çok tüketici hepsi aynı depoda yan yana durabilir. Sıradaki bölüm 6 · Buffer kördür bu fikri başlı başına sabitler; bayt düzeyi 7. bölümde netleşir.

Buffer Kördür

glTF buffer’ını anlamanın en kritik cümlelerinden biri şudur: buffer, verinin ne olduğunu bilmez. Havuzda position, normal, UV veya animasyon verisi yan yana durabilir fakat buffer katmanının kendisi bu ayrımı görmez. Yalnızca ardışık baytlar vardır; «bu bir vertex», «bu bir UV» veya «bu bir anahtar kare» bilgisi sonradan yüklenir.

Bu yüzden buffer’a bakıp «position buffer’ını açtım» demek teknik olarak eksiktir. Açtığınız şey, üst katmanların position olarak yorumladığı bayt aralığıdır. Buffer kördür; gözleri accessor ve bufferView’dedir onlar olmadan ham dosya anlamsız bir hex akışıdır.

Buffer'ın "Kör" Olduğu Gerçekler

Bir 3D dosyasındaki buffer katmanını, devasa bir kütüphanedeki yazısız, isimsiz ve etiketsiz boş sayfalar olarak düşünebilirsiniz. Buffer, içinde sakladığı verinin ontolojik olarak ne anlama geldiğine dair herhangi bir "farkındalık" taşımaz; onun için her şey sadece bir bayt yığınıdır. Aşağıdaki liste, buffer'ın kendi başına asla idrak edemediği, ancak üst katmanların (JSON ve Accessor) yardımıyla bir anlam kazanan kavramsal sınırlarını çizer:

  • "Bu bir vertex (köşe noktasıdır)": Buffer bu verinin uzaydaki bir noktayı temsil ettiğini bilmez. O, sadece 32-bitlik bir sayı grubudur; köşe noktası kavramı, ancak runtime aşamasında ve accessor tipinde bir "konum verisi" (POSITION) olarak tanımlandığı anda vücut bulur.
  • "Bu bir normal vektörüdür": Buffer, taşıdığı verinin bir yüzeyin ışığa nasıl tepki vereceğini belirleyen bir normal vektörü mü, yoksa başka bir şey mi olduğunu ayırt edemez. O sadece float bit desenlerini taşıyan pasif bir taşıyıcıdır; geometrik anlamı, üzerine giydirilen üst katman kurallarıyla gelir.
  • "Bu bir UV koordinatıdır": Buffer için ardışık gelen iki sayı, sadece iki sayıdır. Bunların bir yüzeyin 2D doku koordinatı olduğu bilgisi tamamen dışsaldır; buffer bu veriye bakarak onun bir yüzey üzerinde bir noktayı işaret ettiğini "anlayamaz".
  • "Bu bir animasyon verisidir": Bir mesh verisi ile bir animasyon kanalı (pose, weight veya transform) tamamen aynı buffer havuzunda yan yana huzur içinde yaşayabilirler. Buffer, verinin zamansal bir hareket bilgisini mi yoksa statik bir geometrik yapıyı mı temsil ettiğini birbirinden ayırt etmez; tüm bu etiketleme sorumluluğu üst katmanlara aittir.

Buffer'ın tüm bu kavramlara karşı gösterdiği bu derin "körlük", aslında glTF mimarisinin en büyük tasarım dehalarından biridir. Buffer yalnızca şunu «görür»: 01001100 00110101 11001010 … şeklinde uzayıp giden, kesintisiz, anlamsız bir bayt dizisi. Bu teknik "körlük" tamamen kasıtlı ve stratejiktir; zira ham veriyi organizasyon katmanından bu keskinlikle ayırmak, dosya boyutunu inanılmaz derecede küçültür ve en önemlisi; loader'ların, bellekteki tek bir ArrayBuffer nesnesi üzerinde farklı "gözlükler" (JavaScript tarafındaki Float32Array, Uint16Array gibi typed array görünümleri) takarak, aynı veri bloğunu saniyeler içinde farklı türlerde yorumlamasına ve GPU'ya göndermesine olanak tanır.

Anlam sonradan yüklenir

Anlam yükleme sırası kabaca şöyledir: JSON «hangi accessor?» der → accessor «hangi tip, kaç bileşen, hangi bufferView?» der → bufferView «buffer içinde şu ofset–uzunluk» der → buffer «işte baytlar» der. Buffer zincirin son halkasıdır; soru sormaz, cevap vermez, yalnızca taşır.

Accessor sayfasının temeli burada atılır: accessor, kök buffer’a «gözlük» takar. Bu sayfada gözlüğün mekaniği yok; yalnızca gözlük takılmadan buffer’ın kör kaldığı hissi önemlidir. Liman analojisinde (3. bölüm) konteyner kapalıyken içeriği bilmezsiniz; plan ve etiket gelene kadar yük «sadece ağırlık»tır buffer da öyledir.

Bayt Seviyesinde Düşünmek

Bilgisayar «Vertex #15» diye bir kavram görmez. Gördüğü şey, bellekte veya dosyada sıralanmış baytlardır:

00010111  01100100  10111001  00100010  ...

Buffer tam olarak bu şekilde saklanır: bayt dizisi. Position, normal, UV gibi anlamlar buffer’ın içinde yazılı değildir; anlam, okuma kurallarıyla sonradan verilir 6. bölümdeki «buffer kördür» fikrinin bayt düzeyindeki kanıtı budur.

Bu düşünce biçimi, ileride GPU’ya veri gönderirken de geçerlidir: donanım da «köşe» değil, belirli düzende paketlenmiş bayt blokları görür. Holodepth’te vertex attribute düzeni, VAO/VBO mimarisi ve geometri sayfaları ayrı konulardır; burada yalnızca köprü kurulur: glTF buffer = dosya seviyesindeki ham bayt kaynağı.

Binary Buffer ve Bellek Düşüncesi

«Neden binary?» sorusunun cevabı yalnızca dosya boyutunda değil; bilgisayarın doğal diline yakınlıkta da aranmalıdır. CPU ve RAM, «vertex», «normal» veya «UV» etiketlerini saklamaz. Bellekte duran şey her zaman bayttır glTF buffer katmanı bu gerçeği dosyada erken kabul eder.

Runtime’da loader binary chunk’ı okuduğunda elinde bir ArrayBuffer vardır. Üzerine Float32Array veya Uint16Array «gözlüğü» taktığınızda position veya indeks görünür hale gelir fakat RAM hâlâ aynı baytları tutar; yalnızca yorumlama değişir. JSON’da her float ayrı metin token’ı olurdu; binary’de ise doğrudan IEEE 754 bit desenleri yan yanadır.

RAM’de ne saklanır?

Yanlış zihinsel model: «Bellekte Vertex listesi, Normal listesi, UV listesi ayrı raflarda duruyor.» Dosya seviyesinde glTF buffer çoğu zaman tek şerit; runtime’da motor yine aynı şeridi paylaşır, farklı typed array görünümleriyle segmentlere bakar. İnsan gözü «position array» der; makine
01001100 00110101 … der.

glTF’nin binary tercihi, web’de fetcharrayBuffer() → GPU upload hattına oturması için bilinçli bir seçimdir. Metin parse etmek yerine bayt bloğu taşımak, hem tarayıcı hem oyun motoru dünyasının ortak beklentisidir bir sonraki bölümde bu ortaklık genişler.

Dosya Boyutu: Konsept Karşılaştırması

Tek bir köşe konumu; üç float: X, Y, Z; düşünün. JSON’da bu veri kabaca şöyle görünür:

[
  0.123,
  1.442,
  0.888
]

Her karakter (rakam, nokta, virgül, boşluk, satır sonu) diskte bir bayt tüketir. Ondalık metin hem uzundur hem parse edilmesi gerekir. Aynı üç float, binary’de tipik olarak on iki bayt her biri dört baytlık IEEE 754 float32 olarak paketlenir; virgül, köşeli parantez veya «0.» öneki yoktur.

Burada amaç kesin rakam ezberletmek değil; yoğunluk farkını hissettirmek. Bir vertex’te üç float varken, milyon vertex’te fark katlanır. JSON «okunabilirlik» için bedel öder; binary «yoğunluk ve makine dostu» için tasarlanır. glTF ikisini birleştirerek planı JSON’da, yükü binary’de tutar JSON yapısı · ayrım sayfasındaki formülün pratik gerekçesidir.

Neden Oyun Motorları Binary Kullanır?

glTF’nin binary buffer tercihi izole bir format quirk değildir. Unity, Unreal, Godot, Babylon.js, Three.js hepsi sonuçta ham geometri ve animasyon verisini binary temsil üzerinden taşır ve işler. Dosya uzantıları farklı olsa da (.asset, .uasset, .glb, .bin…) ortak fikir aynıdır: organizasyon metin veya editör formatında, ağır yük binary’de.

Web tarafında Three.js bir glTF yüklediğinde BufferGeometry attribute’ları, arka planda typed array’lerden beslenir yine bayt yorumu. glTF bu ekosistemle uyumlu olmak için JSON + binary hibritini seçmiştir. Okuyucu şunu çıkarmalı: «Bu yalnızca glTF’nin tercihi değil; 3B verinin doğal taşıma biçimine uyum.»

glTF’nin farkı, bu binary yükü açık spesifikasyonla ve web-dostu JSON organizasyonuyla paketlemesidir motor içi proprietary formatlar yerine taşınabilir, dokümante edilmiş bir çift katman. Buffer hissiyatını kavramak, diğer formatları okurken de işinize yarar.

GLB İçerisindeki Binary Chunk

.gltf formatında binary veri çoğu zaman ayrı bir .bin dosyasında durur; .glb ise JSON ile binary’yi tek pakette birleştirir. JSON yapısı sayfasında GLB’nin organizasyonu anlatılmıştı; binary tarafı şu şemayla tamamlanır:

GLB JSON chunk + BIN chunk
.glb tek dosya · iki chunk
  • [JSON chunk] buffers[] referansları · organizasyon
  • [BIN chunk] ham binary veri · bu sayfanın konusu

JSON tarafında gördüğünüz minimal örnek kabaca şöyledir yalnızca «bir buffer var» bilgisini taşır, vertex listesi içermez:

{
  "buffers": [
    {
      "byteLength": 576
    }
  ]
}

byteLength, havuzun kaç bayt olduğunu söyler; içeriğin ne olduğunu değil. İçerik BIN chunk’ta (veya eşlenen .bin dosyasında) durur. Böylece GLB hem taşınabilir tek dosya olur hem de JSON’un okunabilirliğini korur.

Runtime’da Buffer Ne Olur?

Dosyayı yükleyen motor (ör. Three.js GLTFLoader) binary veriyi doğrudan ekrana çizmez. Önce diskten veya ağdan gelen baytlar belleğe alınır, ardından JavaScript tarafında typed array benzeri yapılara dönüştürülür ve son olarak GPU’ya uygun biçimde iletilir.

8. bölümdeki bellek düşüncesi burada somutlaşır: RAM adımında elinizde hâlâ «vertex listesi» yoktur: yalnızca ArrayBuffer. Loader accessor kurallarını uygulayınca aynı bellek bloğu üzerinde Float32Array görünümü «position» gibi hissedilir fakat depolanan hâl bayttır. GPU upload adımı bu baytları donanımın beklediği düzene kopyalar; VAO/VBO detayı bu sayfanın dışındadır.

Zincir kabaca şöyledir:

Runtime Disk → buffer → GPU hattı
  1. Disk / ağ; .glb veya .gltf + .bin

  2. Buffer; ham bayt bloğu çıkarılır

  3. RAM; ArrayBuffer bellekte tutulur

  4. TypedArray ; Float32Array, Uint16Array

  5. GPU upload; sürücü / API’ye kopya

  6. Vertex buffer; çizim için hazır geometri

glTF pipeline sayfaları bu zincirin sahne kurma ucunu (node, materyal, animasyon) anlatır; bu sayfa zincirin veri kaynağı ucunu tamamlar. İkisi birleşince «dosyadan piksele» resim netleşir.

Neden Buffer Kullanılır?

Binary buffer kullanımı, glTF spesifikasyonunun keyfi bir tercihinden ziyade, modern 3D grafik dünyasının ihtiyaç duyduğu katı bir mühendislik gerekliliğidir. Daha önceki bölümlerde, 9. bölüm içerisinde veri yoğunluğunu, 10. bölüm içerisinde ise grafik motorları ve tarayıcılar arasındaki ekosistem ortaklığını detaylandırmıştık. Burada ise, bu mimarinin sunduğu temel faydaları operasyonel verimlilik perspektifinden tek bir çatı altında özetliyoruz:

  • Daha Küçük Dosya Boyutları: Metin tabanlı (text-based) ondalık sayılar, her bir karakterin ASCII/UTF-8 olarak kaydedilmesini gerektirir. Oysa binary temsil, aynı sayısal değeri bayt seviyesinde, bitlerin en verimli şekilde paketlendiği bir formatta tutar. Bu, özellikle ondalıklı sayılarda %50'den fazla tasarruf demektir.
  • Daha Hızlı Ayrıştırma (Parse): JSON formatındaki milyonlarca sayıyı işlemcisi olan bir parser'ın tek tek tokenize etmesi, her karakteri okuyup sayıya dönüştürmesi (type conversion) çok yorucu bir süreçtir. Binary veride ise sistem, bayt bloğunu doğrudan belleğe (memory-mapped file) haritalayabilir; bu da "anında okuma" anlamına gelir.
  • Azalan I/O ve CPU Maliyeti: Büyük sahnelerde verinin okunması, bir darboğazdır. Buffer, metin ayrıştırma katmanını devre dışı bırakarak hem diskin (I/O) hem de işlemcinin (CPU) üzerine binen yükü minimize eder, sahnenin çok daha hızlı ayağa kalkmasını sağlar.
  • GPU Dostu Veri Yapısı: Ekran kartları (GPU), metin dizilerini anlamaz; onlar saf, düzenli ve paketlenmiş bayt akışlarını sever. Binary buffer içerisindeki veriler, uygun yorumlama ile doğrudan GPU'nun veri havuzlarına kopyalanabilir (mem-copy), bu da verinin donanıma ulaşması için en kestirme yoldur.
  • Ölçeklenebilirlik: Yüksek poligon sayılı profesyonel modeller ve karmaşık animasyon sahneleri, JSON-only (sadece JSON içeren) bir tasarımla dijital ortamda pratik olarak yönetilemezdi. Buffer katmanı, modelin karmaşıklığı ne kadar artarsa artsın performansın sabit kalmasını sağlayan temel ölçekleme anahtarıdır.

Önemli bir not: Draco veya Meshopt gibi gelişmiş sıkıştırma teknolojileri, buffer katmanının üzerine uygulanan ayrı, ikincil optimizasyon süreçleridir ve bu sayfada detaylandırılmamıştır. Ancak temel mimari felsefe asla değişmez: Sistemin performansı ne olursa olsun, ağır verilerin binary kanalda, organizasyonel haritanın ise JSON tarafında kalması kuralı korunur. Bu ayrım, glTF'i hem insanlar için okunabilir hem de makineler için aşırı hızlı kılan o eşsiz dengeyi sağlar.

Demo · Binary Memory Explorer

Metin «buffer neden var?» sorusunu cevaplar; bu laboratuvar buffer hissiyatını yaşatır. Ortada basit bir küp var amaç model döndürmek değil; sol paneldeki her segment (position, normal, UV, indices) aynı binary havuzun farklı dilimini temsil eder. Bir segmentin üzerine geldiğinizde küp o veri türünü görselleştirir: köşe vurgusu, normal okları, UV renk haritası veya wireframe indeksleri.

Alttaki renkli şerit, tek byteLength havuzunda segmentlerin yan yana durduğunu gösterir buffer kördür; anlam siz hangi dilime baktığınızda gelir. MRI katman düğmeleri aynı fikri «iç anatomi» sırasıyla tekrarlar. Demo accessor veya bufferView öğretmez; yalnızca «buffer içinde farklı veri türleri yan yana yaşıyor» hissini sabitler.

Demo · Buffer MRI · Three.js r170

Segment veya MRI katmanına gelin · sağda küp güncellenir

Binary buffer · segmentler

Buffer havuzu

Model · veri katmanı geri bildirimi

Bu demo ne hissettirir? JSON sahnenin haritasıydı; binary buffer modelin ham dokusudur position, normal, UV ve indeks aynı havuzda yan yana durur, buffer hangisinin ne olduğunu bilmez. Sonraki sayfalar bu havuza «gözlük» takmayı öğretecek.

JSON ve Buffer Arasındaki İş Bölümü

glTF anatomisinin ilk iki dosya yapısı sayfası şu tabloda özetlenir. Sonraki adımlar (accessor, bufferView) tablodaki «yorumlama» satırını derinleştirir.

Katman Görev
JSON Organizasyon · indeksler · sahne planı
Buffer Ham veri · bayt havuzu
Accessor Veriyi yorumlama (tip, boyut, ofset sonraki sayfa)
Runtime Sahneyi oluşturma · motor ve GPU

JSON yapısı sorusunu cevapladı: «Sahne nasıl organize edilir?» Bu sayfa ikinci soruyu cevaplar: «Gerçek veri nerede durur?» cevap: binary buffer içinde, JSON ise o veriye giden yolu tarif eden haritadır.

Sıradaki mantıksal adım: bu bayt havuzunun hangi dilimlerinin nasıl okunacağı. Bunun için bufferView ve accessor sayfalarına geçilir; her biri yalnızca kendi katmanına odaklanır.

Sayfayı kapatırken hedeflenen his budur: «JSON sahnenin haritasıydı. Binary buffer ise modelin gerçek dokusu, kemiği ve ham maddesi. Sahneyi oluşturan sayısal yükün tamamı burada yaşar anlamı ise bir sonraki adımda takacağımız gözlükle okuyacağız.»