Three.js · İleri geometri
Interleaved Buffers: Veri Paketleme Sanatı ve GPU Bant Genişliği
Stride, offset ve bellek yerelliği ile CPU–GPU köprüsünü verimli kullanmak
Modern grafik programlamada hız, sadece ne kadar veri gönderdiğinizle değil, o veriyi GPU belleğine nasıl yerleştirdiğinizle ilgilidir. Advanced Geometri serisinin bu bölümünde, veriyi “dağınık” tutmak yerine içe içe (interleaved) dizmenin, işlemci ve grafik kartı arasındaki dar köprüde trafik akışını nasıl iyileştirebileceğini inceliyoruz.
Ön bilgi için Attributes & buffer’lar; indeks düzeni için Indexed vs non-indexed; çoklu kopya için Instancing.
Bellek mimarisi: sıralı (sequential) ve içe içe (interleaved)
Normal bir BufferGeometry yapısında veriler çoğu zaman ayrı
attribute dizilerinde tutulur: pozisyonlar bir
Float32BufferAttribute,
renkler başka bir buffer’da, UV koordinatları yine ayrı bir blokta. Bu düzen okunması kolay
ve araç zinciriyle uyumludur; fakat GPU bir vertex işlerken birden fazla bellek bölgesine
sırayla dokunmak zorunda kalabilir.
Non-interleaved (sıralı) yapı
Bu yapıda GPU, tek bir köşe için veriyi toplarken bellekte atlama yapabilir:
- “Pozisyon listesinin 0. elemanını getir.” (ör. bellek bölgesi A)
- “UV listesinin 0. elemanını getir.” (ör. bellek bölgesi B)
- “Normal listesinin 0. elemanını getir.” (ör. bellek bölgesi C)
Bu tür atlama, cache miss (önbellek ıskalaması) riskini artırır; çekirdek veriyi beklerken pipeline verimli dolmayabilir. Özellikle yoğun vertex iş yükünde maliyet hissedilir.
Interleaved (içe içe) yapı
Interleaved düzende, bir köşeye ait kanallar bellekte bitişik bir paket olarak durur — tipik olarak pozisyon, UV, normal vb. aynı “stride” bloğu içinde sırayla dizilir. Kavramsal örnek (her köşe için kanal sırası sabit varsayılmıştır):
[ Px, Py, Pz, U, V, Nx, Ny, Nz, Px, Py, Pz, U, V, Nx, Ny, Nz, … ]
GPU bir köşe okumasına başladığında, o köşeye ait alanlar aynı bellek yakınlığında bulunur. Buna memory locality (bellek yerelliği) denir; önbellek hatları için elverişlidir.
Teknik kavramlar: stride ve offset
Interleaved buffer kullanırken GPU’ya “paketin şeklini” anlatmanız gerekir. İki kritik terim:
-
Stride (adım): Bir köşe kaydının toplam byte veya float cinsinden
uzunluğu. Örneğin pozisyon (3 float) + UV (2 float) kullanıyorsanız, bir sonraki köşeye
geçmek için paket başına 5 float atlanır — stride burada 5 float’tır
(byte cinsinden:
5 × 4 = 20byte, yalnızca bu iki kanal için). -
Offset (sapma): Aynı paket içinde belirli bir attribute’un nereden
başladığı. Pozisyonlar paketin başındaysa offset
0; UV’ler hemen ardından geliyorsa offset3(üç float atlayarak) gibi düşünülür.
Aşağıdaki canlı demoda örnek paket pozisyon (3) + UV (2) + normal (3) olup stride 8 float (32 byte) seçilmiştir; yalnızca pozisyon + UV düşünüldüğünde çıkan 5 float örneği ayrı bir sadeleştirmedir.
Three.js tarafında bu mantık InterleavedBuffer ile tek ham
TypedArray üzerinde kurulur; her kanal için
InterleavedBufferAttribute
ilgili itemSize, offset ve paylaşılan stride ile
tanımlanır.
Canlı demo: Sık düzlem ızgarası, sıralı vs interleaved
Aşağıdaki model yoğun bir PlaneGeometry ızgarasıdır (20×20 segment → 441
köşe). Geometri aynı kalır; yalnızca GPU’ya giden buffer düzeni değişir.
Şematik şerit, sıralı modda pozisyon / UV / normal bloklarının ayrı ayrı dizildiğini;
interleaved modda ise her köşe için 8 float’lık paket (3+2+3) tekrarını
gösterir. Ok, sıralı modda bloklar arasında zıplayarak, interleaved modda
ise
düz süzülerek gider; vertex noktalarındaki hafif titreşim / nabız da
tamamen
öğretici bir vurgudur (ölçüm değildir). HUD’daki 3 fetch → 1 fetch ifadesi
GPU mental modelini hatırlatır.
Aşağıdaki demo sabitleri,
doc-interleaved-demo.js içindeki initInterleavedGridDemo ve
buildPlaneGeometries ile aynı rakamları özetler.
Bellek şeridi (şematik · veri akışı)
Altın cümle
Interleaved, GPU’nun veriyi tek seferde okumasını sağlar — performans buradan gelir.
Özet
3B sahnede görünen ızgara aynıdır; fark BufferGeometry
attribute’larının
arka planda ayrı mı yoksa InterleavedBuffer üzerinde paketlenmiş
mi sunulduğundadır. Yoğun köşe sayısında bu ayrım, okunabilirlik ile bant
genişliği / önbellek davranışı arasındaki klasik takastır.
Demo sabitleri tablosu (Plane grid · buffer)
SEG = 20 ile düzlem (20+1)² = 441 köşe üretir; sıralı ve
interleaved geometriler aynı indeks ve aynı sayıda köşe paylaşır, yalnızca attribute
düzeni değişir.
| Sahne / rol | Parametre | Değer | Tür |
|---|---|---|---|
| Izgara | SEG |
20 → köşe 21×21 = 441 |
🔒 Sabit |
| Temel düzlem | PlaneGeometry |
genişlik / yükseklik 2.35 × 2.35 · segment
SEG × SEG · computeVertexNormals()
|
🔒 Sabit |
| Paket | STRIDE_FLOATS / bayt |
8 float (3+2+3) · 32 byte |
🔒 Sabit |
| Interleaved attribute | InterleavedBufferAttribute |
position · item 3 · offset 0 ·
normalized: false · uv · 2 ·
3 · false · normal · 3 ·
5 · true
|
🔒 Sabit |
| Sıralı kopya | BufferGeometry |
position / uv / normal ayrı
BufferAttribute · indeks base.index klonu
|
🔒 Sabit |
| Işık | HemisphereLight |
0xa7d8ff / 0x151820 · 1.15 |
🔒 Sabit |
| Işık | AmbientLight / DirectionalLight |
beyaz ortam 0.38 · yönlü 2.35 · konum
(3.5, 5.5, 4)
|
🔒 Sabit |
| Kamera | PerspectiveCamera |
FOV 40 · yakın/uzak 0.08 / 80 · konum
(0, 2.15, 3.35) · lookAt(0,0,0)
|
🔒 Sabit |
| Pivot | Group.rotation |
başlangıç x = -π/2 + 0.28 · z = 0.12 · her kare
y += dt × 0.22
|
🔒 Sabit |
| Yüzey | MeshStandardMaterial |
0x4a8fb8 · metalness 0.12 ·
roughness 0.42
· DoubleSide · flatShading: false
|
🔒 Sabit |
| Tel kafes | LineBasicMaterial |
0x6ec8ff · opaklık 0.35 |
🔒 Sabit |
| Vertex noktaları | PointsMaterial |
taban boy 0.045 · opaklık 0.82 ·
sizeAttenuation: true · moda göre kare içi sinüs ile boy/opaklık
animasyonu
|
🔒 + ↔ UI |
| Renderer | makeRenderer |
ACESFilmicToneMapping · exposure 1.42 ·
SRGBColorSpace · setPixelRatio(min(dpr,2)) ·
setSize(…, true)
|
🔒 Sabit |
| Kök özniteliği | data-ilv-layout |
sequential / interleaved (mod değişince güncellenir)
|
↔ JS |
| UI varsayılanları | segment / onay | Sequential · tel kafes açık · vertex noktaları açık | ↔ HTML |
Önemli kod kesiti
Sıralı geometri ayrı buffer’lardan; interleaved geometri tek
Float32Array + InterleavedBuffer üzerinden kurulur.
const SEG = 20;
const STRIDE_FLOATS = 8;
const base = new THREE.PlaneGeometry(2.35, 2.35, SEG, SEG);
base.computeVertexNormals();
// seq: ayrı BufferAttribute klonları + index — dosyanın üstünde; burada interleaved paket:
const pos = base.attributes.position;
const uv = base.attributes.uv;
const nrm = base.attributes.normal;
const n = pos.count;
const arr = new Float32Array(n * STRIDE_FLOATS);
for (let i = 0; i < n; i++) {
let o = i * STRIDE_FLOATS;
arr[o++] = pos.getX(i);
arr[o++] = pos.getY(i);
arr[o++] = pos.getZ(i);
arr[o++] = uv.getX(i);
arr[o++] = uv.getY(i);
arr[o++] = nrm.getX(i);
arr[o++] = nrm.getY(i);
arr[o++] = nrm.getZ(i);
}
const ib = new THREE.InterleavedBuffer(arr, STRIDE_FLOATS);
const ilv = new THREE.BufferGeometry();
if (base.index) ilv.setIndex(base.index.clone());
ilv.setAttribute("position", new THREE.InterleavedBufferAttribute(ib, 3, 0, false));
ilv.setAttribute("uv", new THREE.InterleavedBufferAttribute(ib, 2, 3, false));
ilv.setAttribute("normal", new THREE.InterleavedBufferAttribute(ib, 3, 5, true));
function applyMode() {
const g = sequentialMode ? GEO_SEQ : GEO_ILV;
mesh.geometry = g;
points.geometry = g;
wire.geometry = new THREE.WireframeGeometry(g);
}
Neden interleaved kullanılır?
Holodepth gibi yüksek performans hedefleyen uygulamalarda — özellikle binlerce benzer örneğin veya büyük parçacık benzeri veri akışlarının olduğu sahnelerde — paketleme fark yaratabilir:
- CPU–GPU transferi: Tek büyük sürekli blok halinde veri göndermek, çok sayıda küçük parçaya bölmekten genelde daha verimlidir; sürücü ve API katmanında daha az fragmentasyon hedeflenir.
- Önbellek dostu erişim: Bitişik düzen, L1/L2 önbellek davranışıyla daha uyumludur; vertex shader bir köşeyi işlerken sıradaki köşeye geçiş daha “düz” olabilir.
- Güncelleme maliyeti: Pozisyon ve renk (veya başka kanallar) her karede birlikte değişiyorsa, tek buffer üzerinde güncelleme yapmak birden fazla buffer’ı senkron tutmaya göre daha basit ve hataya daha az açık olabilir.
HoloDepth uygulama notları
Three.js’te InterleavedBuffer ve InterleavedBufferAttribute ile
bu yapı kurulur. Pratikte sık görülen kullanım: özel shader (RawShaderMaterial
veya özel ShaderMaterial) ile attribute düzenini birebir kontrol ettiğiniz
hatlar, ya da Lidar nokta bulutu, yoğun CAD verisi gibi büyük ve homojen
vertex akışları.
Görsel / mimari ipucu: Interleaved düzen, instancing ile birleştiğinde tarayıcı tabanlı deneyimlerde “masaüstü oyun” hissi veren sahnelerde sık kullanılan bir optimizasyon katmanıdır — tek başına sihir değildir; draw çağrısı, materyal ve sahne düzeniyle birlikte düşünülmelidir.
Özet: ne zaman kullanmalı?
| Bağlam | Öneri |
|---|---|
| Standart uygulama, hızlı prototip, okunabilirlik öncelikli kod | Çoğu zaman klasik BufferGeometry + ayrı attribute’lar yeterli
ve bakımı kolaydır. |
| HoloDepth “pro” hattı: bant genişliği ve vertex güncellemesi kritik | Profil çıkarın; darboğaz vertex ise interleaved + (gerekiyorsa) instancing / özel shader ile paketleyin. |