holodepth

Three.js · Geometri

Attributes ve Buffer’lar (veri katmanları)

Geometri = GPU’ya giden sayılar

Three.js’te bir geometri, GPU belleğine aktarılan bir dizi sayıdan ibarettir. Bu sayılar rastgele değil; BufferAttribute adı verilen yapılarda, belirli bir düzende (interleaved veya sequential) saklanır. GPU, bu buffer’lar üzerinden okuma yaparak her vertex’i ekrandaki piksellere dönüştürür; “şekil” dediğiniz şey, aslında bu sayıların okunma biçimidir.

Bu sayfa attribute ve buffer katmanına odaklanır: vertex başına hangi kanallar vardır, Float32Array ile nasıl paketlenir, sequential / interleaved farkı nedir. BufferGeometry’nin genel rolü ve vertex → yüzey çerçevesi Geometri giriş sayfasındadır; primitive üretimi ve segment seçimi Primitive geometriler konusuna bırakılır; burada tekrar etmiyoruz.

Manuel geometri yazımı ve özel buffer kurulumu Custom buffer geometri ile devam eder; ileri seviye interleaved ayrıntısı Interleaved buffers konusundadır. Aşağıda özel attribute örneği, bellek sayacı ve mobil bütçe notu bulunur.

Attribute nedir?

Attribute = her vertex için tekrar eden veri bloğu. “Modelin verisi” dediğiniz şey, aslında vertex sayısı boyunca uzanan sütunlardır: position, normal, uv, color … Aynı satır indeksi (ör. 42. vertex) tüm sütunlarda o köşeyi tanımlar; tablo gibi düşünün: satır = vertex, sütun = attribute kanalı.

Standart kanallar ne taşır?

Çoğu BufferGeometry en az position taşır; gerçekçi ışık ve doku için normal ve uv eklenir. color isteğe bağlıdır; vertex başına renk veya varyasyon gerektiğinde devreye girer. Hepsi aynı vertex indeksinde hizalanır; Typed Arrays bölümündeki itemSize değerleri kanala göre değişir (3, 3, 2, 3 …).

  • position: Şeklin iskeleti — köşenin model (yerel) veya dünya uzayındaki (x, y, z) adresi. Olmadan mesh çizilemez; vertex shader buradan başlar, modelViewMatrix ve projectionMatrix ile ekrana taşınır. Yerel / dünya ayrımı Object3D transform katmanındadır — geometri yalnızca “köşe adreslerini” verir.
  • normal: Yüzeyin o noktadaki “baktığı yön” vektörü; ışık, gölge ve PBR hesapları buna göre yapılır. Düz renkli materyalde bile eksik normal, yüzeyin “düz veya ters” görünmesine yol açar. Elle yazılmış geometride geometry.computeVertexNormals() çoğu zaman yeterlidir; karmaşık ağlarda DCC’den gelen normal’ler tercih edilir — normal map materyal kanalından ayrıdır ( normal map yüzey pürüzünü taklit eder).
  • uv: 2B doku koordinatı (u, v, genelde 0–1 aralığı) — hangi pikselin bu köşeye yapışacağını söyler; materyaldeki map, normalMap, roughnessMap vb. bu koordinatla örneklenir. UV olmadan düz renk çalışır; desen, logo veya PBR dokuları için şarttır. Sarma ve tekrar mantığı UV mapping konusundadır — burada yalnızca geometrideki uv attribute’una odaklanıyoruz.
  • color: (İsteğe bağlı) Vertex başına renk veya varyasyon; basit gölgelendirme, MeshBasicMaterial ile vertex color, partikül veya özel shader girdisi için kullanılır — her mesh’te şart değildir. Materyalde vertexColors: true açılmadan buffer okunmaz; kanal yoksa materyal rengi / doku yeterlidir.

Özet: position zorunlu iskelet; normal + uv çoğu gerçekçi sahne için pratik zorunluluk; color özel efekt. Kanal rollerinin kısa tablosu Geometri giriş · veri türleri sayfasında; burada paketleme ve bellek tarafına devam ediyoruz. Vertex → yüzey çerçevesi Geometri giriş sayfasındadır.

GPU her vertex için ne sorar?

Üstteki liste her kanalın ne taşıdığını anlatır; bu alt başlık ise draw anında GPU’nun vertex shader’a ne okuduğunu hatırlatır. Her draw çağrısında, indekslenmiş veya sıralı her köşe için attribute buffer’dan bir satır çekilir ve shader girdisi olur; binlerce vertex varsa bu döngü binlerce kez tekrarlanır.

Vertex shader kabaca şu soruları yanıtlar; cevaplar attribute’lardan gelir:

  • Position: Neredesin? — Okunan position önce model, sonra görüntü uzayına taşınır; çıktı gl_Position ile ekrandaki konuma dönüşür. Kanal tanımı yukarıda.
  • Normal: Nereye bakıyorsun? (yüzey yönü) — Işık yönü ve gölgelendirme bu vektöre göre hesaplanır; normal eksik veya tersse yüzey “içe dönük” veya düz görünür. Dönüşüm matrisleri normali de döndürür (normalMatrix).
  • UV: Üzerine hangi doku (texture) parçasını giyeceksin? — Vertex shader UV’yi çoğu zaman fragment’a iletir (varying); asıl doku örnekleme fragment aşamasında, materyalin map kanallarıyla yapılır.
  • Color: (Varsa) Senin rengin ne? — color attribute yoksa bu soru sorulmaz; varsa vertex rengi materyal rengi veya doku ile karıştırılabilir ( vertexColors: true).

Özet: attribute’lar vertex shader’ın ham girdisi; matris çarpımları onları ekrana taşır. Üçgenler oluştuktan sonra fragment shader piksel başına renk, derinlik ve doku hesaplar; geometri buffer’ları o aşamada yeniden okunmaz, vertex aşamasında okunan değerler interpolate edilir. Tam hat Renderer · pipeline sayfasında; burada yalnızca “köşe başına okunan dört soru” vurgulanır.

Vertex sayısı ve Three.js isimlendirmesi

GPU her vertex için attribute’ları tekrar tekrar işler. 1000 vertex varsa position, normal, uv gibi her aktif kanal da 1000 kayıt üzerinden okunur; vertex sayısı, her sütunun “yüksekliğini” belirler. Üçgen sayısı arttıkça çizim maliyeti artar; attribute dizilerinin uzunluğu ise benzersiz köşe sayısına bağlıdır.

Three.js tarafında kanallar geometride isimle tutulur: geometry.attributes.position, geometry.attributes.normal, geometry.attributes.uv, geometry.attributes.color vb. Eksik attribute eklemek veya özel kanal tanımlamak sonraki bölümde setAttribute ile yapılır. Position / normal / UV / index rollerinin kısa tablosu Geometri giriş · veri türleri sayfasındadır; burada tekrar etmiyoruz.

Paylaşılan köşeler için geometry.index aynı vertex satırını birden fazla üçgende kullanır; böylece attribute verisi tekrar yazılmaz. Attribute boyutu çoğu zaman position.count ile okunur; bellek tahmini için bellek sayacına bakın.

Typed Arrays ve BufferAttribute

JavaScript’in standart dizileri (Array) esnektir ama grafik tarafında veri transferi için pahalıdır: elemanlar farklı tiplerde olabilir, bellek düzeni GPU’nun beklediği gibi ardışık değildir. Bu yüzden TypedArray kullanılır; özellikle Float32Array: bellekte kesintisiz bir blok kaplar; GPU veriyi tek hamlede “yutabilir”. Önceki bölümdeki attribute sütunları, pratikte bu typed array’lerin geometriye bağlanmış halidir.

Float32Array: CPU’dan GPU’ya köprü

Koordinat ve çoğu shader girdisi 32-bit kayan nokta ile yeterlidir; bu yüzden Float32Array en sık görülen seçimdir. Her sayı bellekte 4 byte yer kaplar; örneğin 10.000 vertex’lik yalnızca position kanalı kabaca 120 KB ham veridir (meta veri ve diğer kanallar hariç). Tahmin için bellek sayacı kullanılabilir.

İndeks listesi için çoğu zaman Uint16Array veya büyük mesh’lerde Uint32Array tercih edilir; paylaşımlı köşe mantığı Attribute nedir? bölümünde kısaca geçilmişti.

BufferAttribute ve itemSize

Ham dizi tek başına yetmez; Three.js’e “bu sayıları nasıl okuyacağını” söyleyen sarmalayıcı BufferAttribute’dur:

new THREE.BufferAttribute(typedArray, itemSize)

Item size, bir attribute’un kaç bileşenden oluştuğunu belirtir; GPU’ya “her vertex kaydı şu kadar ardışık sayıdan oluşur” demenin yoludur. Dizi uzunluğu itemSize’a bölünürse vertex sayısı çıkar: attr.count === typedArray.length / itemSize.

Kanal adı ile itemSize eşleşmesi ( standart kanallar) şöyledir; yanlış itemSize, GPU’nun sayıları yanlış gruplamasına yol açar (kaymış veya patlamış mesh):

  • position: itemSize 3x, y, z
  • uv: itemSize 2u, v
  • normal: itemSize 3 — yüzey yönü
  • Özel kanal: Örneğin aSize için itemSize 1 — tek skaler per vertex; uygulama bölümünde gösterilir

setAttribute: geometriye bağlama

Typed array hazır olduktan sonra kanal ismiyle geometriye eklenir. İsim, shader ve Three.js tarafında attribute’u tanımlar; aynı geometriye normal, uv gibi başka kanallar da aynı kalıpla bağlanır.

Aşağıdaki örnek yalnızca position ile tek üçgenin üç köşesini tanımlar:

const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
  -1, -1, 1,
   1, -1, 1,
   1,  1, 1,
]);

geometry.setAttribute(
  'position',
  new THREE.BufferAttribute(vertices, 3)
);

Adım adım okuma:

  • BufferGeometry: Boş bir geometri kabı; attribute’lar sonradan setAttribute ile takılır.
  • Float32Array: Dokuz sayı = 3 vertex × itemSize 3. Dizi düz akar; gruplama BufferAttribute’un ikinci argümanı ( 3) ile yapılır — vertices.length / 3 === 3 vertex.
  • setAttribute('position', …): Bu kanalı “konum” olarak kaydeder; erişim geometry.attributes.position üzerinden olur.

Bu örnek tek üçgen çizer (varsayılan drawRange ile). Daha karmaşık mesh’lerde aynı position dizisine ek olarak geometry.setIndex(...) ile köşe paylaşımı tanımlanır; indeks ayrı bir buffer’dır, itemSize genelde 1 ( Attribute nedir? bölümünde geçmişti). Işık için çoğu zaman geometry.computeVertexNormals() veya elle normal attribute’u eklenir; tam manuel akış Geometri giriş · manuel veri örneğinde; burada yalnızca bağlama kalıbı gösterilir.

BoxGeometry gibi primitive’ler aynı yapıyı sizin yerinize üretir; Primitive geometriler konusunda parametre verirsiniz, içeride yine setAttribute benzeri bir yol izlenir. Statik mesh’te veri bir kez yüklenir; her karede dizi değişiyorsa attribute.needsUpdate = true ve çoğu zaman DynamicDraw; buffer kullanım tablosu. Özel geometri ve çok kanallı kurulum Custom buffer geometri ile devam eder.

Teknik tablo: buffer tipleri ve kullanım amaçları

Buffer türü İçerik GPU rolü
StaticDraw Çoğu geometri için varsayılan senaryo (küp, küre, statik mesh). Default düşün: Veri genelde bir kez yüklenir, çok hızlı okunur.
DynamicDraw Vertex sürekli değişiyorsa (animasyon, dalga, deformasyon). Önemli: Sık güncelleme için; CPU→GPU akışı vardır.
StreamDraw Çok nadir güncellenen veriler. Nadir: Bazı akış senaryoları için; çoğu projede gerekmez.

GPU’ya veri gönderme: interleaved vs sequential

Profesyonel projelerde veriyi nasıl dizdiğiniz, GPU’nun önbellek (cache) davranışını etkiler. İki yöntem de aynı sayıları taşır; aynı position, normal, uv kanalları ( standart kanallar); fark, vertex shader bir köşeyi işlerken bellekte ne kadar sıçradığınızdır. Sequential “her kanal ayrı rafta” okumak; interleaved “bir köşenin tüm evrakı arka arkaya” okumak gibidir.

Kurulum ayrıntısı (InterleavedBuffer, InterleavedBufferAttribute, stride, offset) Interleaved buffers konusunda kalır; burada kavram, artı/eksi ve karar.

Sequential (ardışık) — Three.js varsayılanı

Önce tüm konumlar, sonra tüm normaller yazılır; uv ve diğer kanallar ayrı dizilerde devam eder. Her kanal kendi BufferAttribute ve çoğu zaman ayrı GPU buffer’ıdır; setAttribute('position', …), setAttribute('normal', …) kalıbı ( önceki bölüm) doğrudan buna uyar. CPU’da üretmek, debug etmek ve yalnızca position animasyonu yapmak kolaydır; Three.js’in doğal yolu budur.

  • Artı: Okunaklı kod; tek kanalı güncellerken diğer dizilere dokunmazsınız; Holodepth öğrenme ve çoğu web sahnesi için yeterlidir.
  • Eksi: Çok büyük mesh’lerde GPU, bir vertex için position, normal ve uv okurken bellekte uzak bölgelere atlayabilir — cache isabetsizliği teorik olarak artar (her GPU’da ölçülmeli).

Interleaved (iç içe)

Bir vertex’in konumu, normali, rengi bellekte yan yana yazılır: [pos, norm, col, pos, norm, col, …]. Tek büyük typed array veya paylaşılan buffer üzerinde her attribute farklı offset ile okunur. GPU bir vertex işlerken o köşeye ait alanların çoğu aynı bellek bandında kalır; büyük, statik, tek sefer yüklenen veri setlerinde cache performansı daha iyi olabilir; her cihazda ve her sahne tipinde garanti değildir.

  • Artı: Vertex başına yerellik; devasa statik mesh’lerde profiler ile görülen kazançlar.
  • Eksi: Paketleme, export ve tek kanal güncelleme zahmetli; stride/offset hatası mesh’i sessizce bozar — önce sequential ile doğrulayıp sonra dönüştürmek daha güvenlidir.

Kısa karar rehberi

“Önce doğru çalışsın” diyorsanız sequential ile kalın. Veri setiniz devasa, erişim paterni net ve hedef cihazda profil ile kazanç ölçülmüşse interleaved bazı GPU’larda daha iyi çıkabilir; varsayım yerine kare süresi ve bellek ölçün.

  • Sequential tercih: Prototip, öğrenme, sık attribute güncellemesi ( needsUpdate, dalga, morph), küçük–orta mesh, birden fazla kaynaktan birleşen geometri.
  • Interleaved tercih: Tek büyük statik mesh (terrain, scan), attribute seti sabit, mobil veya düşük bant genişliğinde ölçülen darboğaz.
  • Ölçüm: Layout değişikliği her platformda aynı sonucu vermez; ham attribute boyutu için bellek sayacı, performans için hedef cihazda FPS / frame time.

Aynı verinin iki dizilimini satır satır karşılaştırmak için hemen altındaki görsel şema bölümüne geçin; tablo orada, burada yorum. Kanal anlamı Attribute nedir?; typed array ve setAttribute Typed Arrays bölümünde; burada yalnızca bellek düzeni farkı anlatılır.

Görsel şema: aynı veri, iki farklı dizilim

Aşağıdaki şema, GPU’nun bellekte “nasıl yürüdüğünü” hissettirir. Solda ardışık kanallar, sağda vertex başına paketlenmiş kayıtlar:

Sequential → yönetimi kolay (Three.js default) · Interleaved → cache dostu olabilir (büyük veri).

Sequential (kanal kanal) Interleaved (vertex vertex)
[pos][pos][pos][pos]...
[normal][normal][normal][normal]...
[uv][uv][uv][uv]...
[pos norm uv][pos norm uv][pos norm uv]...
[pos norm uv][pos norm uv][pos norm uv]...

Uygulama: özel bir attribute eklemek

Sadece konumu değil, her vertex’e rastgele bir “boyut” veya “hız” bilgisi ekleyip shader’da kullanmak için, yeni bir TypedArray oluşturup geometriye isimle bağlarsınız. GPU bu veriyi shader içinde attribute float aSize olarak tanır.

Bağlantı şu: bu değer, vertex shader’da bir hesapta “çarpan” olur. Örneğin point cloud çiziyorsanız: gl_PointSize = aSize * 10.0;

const geometry = new THREE.BufferGeometry();
const count = 5000;

// Her vertex için 1 değer (itemSize: 1)
const sizes = new Float32Array(count);

for (let i = 0; i < count; i++) {
    sizes[i] = Math.random(); // 0 … 1 arası rastgele ölçek
}

// 'aSize' adında özel bir attribute oluşturuyoruz
geometry.setAttribute('aSize', new THREE.BufferAttribute(sizes, 1));

// GPU bu veriyi shader içerisinde 'attribute float aSize' olarak tanıyacak.
attribute float aSize;

void main() {
  // ... position transform ...
  gl_PointSize = aSize * 10.0;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

Mini araç: attribute belleğini anında hesapla

“Bu geometri kaç KB/MB yer tutar?” sorusu pratikte en çok iş yapan refleks. Aşağıdaki araç, seçtiğiniz vertex sayısı ve attribute seti için yaklaşık ham veri boyutunu hesaplar; Three.js’te sık kullanılan Float32Array / Float32 varsayımıyla (her bileşen 4 byte).

Bu bir GPU sürücü raporu değildir: index buffer, geometri meta verisi, InterleavedBuffer hizalama dolgusu ve doku belleği dahil değildir; ama “sadece bu dört attribute ile kaç MB büyür?” tahmini için hızlı bir cetvel işlevi görür.

Attribute bellek sayacı

Float32 · canlı tahmin

Hangi attribute’lar hesaba katılsın?
Tahmini toplam

Satır satır (Float32)

  • position
  • normal
  • uv
  • color

index buffer ve geometri ötesi bellek bu toplama dahil değildir.

Holodepth notu: bellek bütçesi

Attribute’lar çok hızlı büyür: önce ihtiyacı ölç

Her bir Float32Array elemanı bellekte 4 byte yer kaplar. 1 milyon vertex’li bir modelde sadece position verisi bile yaklaşık 12 MB (\(1{,}000{,}000 \times 3 \times 4\) byte) yer tutar.

Normaller, UV’ler ve diğer attribute’lar eklendikçe bu miktar hızla artar. Mobil cihazlarda çökme yaşamamak için her zaman ihtiyacınız olmayan attribute’ları temizleyin ve gerekiyorsa LOD / basitleştirme stratejileri kullanın.