holodepth

Three.js · Kamera türleri

ArrayCamera (dizi kamera)

ArrayCamera — tek render, çoklu kamera görünümü

ArrayCamera, sahneyi önceden tanımlanmış bir dizi alt kamera ile tek bir renderer.render(scene, camera) çağrısında ekranın farklı bölgelerine eşzamanlı olarak çizmeyi kolaylaştırır. Dört ayrı bakış için dört kez ayrı ayrı render döngüsü kurmak yerine, alt kameralar ve viewport ile bölünmüş ekran (split-screen) mantığını tek geçişte yönetirsiniz — CPU tarafında daha az çağrı, GPU tarafında düzenli çoklu görünüm akışı. Genel kamera modeli için Kamera Giriş; tek bakış perspektif için PerspectiveCamera.

Çoklu bakış açısı ve split-screen mantığı

ArrayCamera, ekranın belirli bölgelerini farklı alt kameralara tahsis eder. Her alt kamera (sub-camera), yalnızca kendisine ayrılan viewport dikdörtgeni içinde sahneyi görür; böylece dört oyunculu bölünme, izleme monitörü veya dikiz aynası düzeni tek sahne üzerinde birleşir.

Kullanım senaryoları

  • Yarış / simülasyon: dikiz aynası veya cockpit içi ek görünümler
  • Çok oyunculu split-screen: aynı karede birden fazla bakış
  • Güvenlik / gözetim: tek tuvalde dört kamera akışı
  • Karmaşık VR / çoklu ekran prototipleri: bölgelere ayrılmış görünüm

Kritik nüans: tek render() çağrısı, içeride birden çok geçiş

ArrayCamera ile yaptığınız tek renderer.render(scene, arrayCamera) çağrısı, uygulama düzeyinde “tek draw” anlamına gelmez. WebGLRenderer içinde her alt kamera için sırayla viewport ayarlanır ve çizim listesi o bakış açısıyla yeniden işlenir — yani motor tarafında internal bir döngü vardır. Kazanç çoğunlukla API ve akış sadeleşmesi (tek çağrı, aynı kare zamanı, daha az el yapımı senkron) ve doğru ayarlandığında daha tutarlı pencerelemedir; “GPU yükü görünüm sayısıyla çarpılır” satırıyla da örtüşür.

Bu desen WebXR stereo mantığına da yakındır: her göz için ayrı projeksiyon ve dikdörtgen, tek kare ömründe eşleştirilir; ArrayCamera köken itibarıyla bu tür çok bölgeli, çok bakışlı düzenlere dayanır — VR dışında split-screen ve izleme panelinde aynı okuma geçerlidir.

Yapısal anatomi: alt kameralar ve viewport

ArrayCamera, bir dizi PerspectiveCamera (nadiren OrthographicCamera) nesnesini bir arada tutan bir kapsayıcı kameradır. En kritik parça, her alt kameranın tuval üzerindeki yerini belirleyen viewport ayarıdır.

  • Viewport: Genelde tuvale göre normalize (0–1) veya piksel biriminde verilen (x, y, genişlik, yükseklik) dörtlüsü; GPU’ya “çizimi yalnızca bu dikdörtgen içine sığdır” emrini iletir (makaslama / scissor ile birlikte düşünülür).
  • Senkronizasyon: Tüm alt kameralar aynı sahneyi paylaşır; her birinin position, rotation, fov gibi özellikleri bağımsızdır, böylece aynı anda farklı bakışlar üretilir.

Normalize tuval koordinatları (WebGL): Viewport genelde tuvale göre 0–1 arası ifade edilir; (0, 0) köşe sol alt, (1, 1) sağ üsttür (y ekseni yukarı doğru büyür). CSS ile üst/sol köşeden konumlandırma alışkanlığı olanlar için bu fark önemlidir: aynı “üstteki panel” WebGL’de yüksek y değerine karşılık gelir.

İleri: viewport ve scissor

Viewport: Projeksiyonu bu dikdörtgene sığdırır — yani “kamera görüntüsü hangi piksel alanına yayılacak?”

Scissor (makas) testi: Çizimin yazılacağı piksel alanını kırpar; çoklu alt render’da genelde viewport ile birlikte kullanılır ki bir alt görünüm komşu bölgelere taşmasın. Three.js çoklu bakış yollarında bu ikisi pratikte sık yan yana gelir.

Viewport + scissor birlikte: Viewport, NDC’den ekrana projeksiyonun hangi dikdörtgende ölçekleneceğini söyler; scissor ise raster aşamasında renk ve derinlik yazımını hangi piksel alt kümesine kısıtlar. Döngülü render() yazarken ikisini çoğu zaman aynı piksel dikdörtgeninde eşleştirmek, komşu panele taşmayı ve “hayalet” fragment’leri kesmek için en güvenli kalıptır.

Derinlik tamponu (tek hedef): Varsayılan çerçeve tamponunda tipik olarak tek bir depth buffer paylaşılır; her alt geçiş yalnızca kendi viewport alanındaki piksellere yazar. Paneller birbirinden ayrık olduğu sürece bir panelin derinlik değeri komşunun piksellerini budamaz — bu, çoklu bakışta doğruluk için önemlidir. Aynı panel içinde üst üste binen yüzeylerde z-fighting algısı yine oluşabilir; çoklu bakış bunu tek başına çözmez.

Etkileşimli demo: tek sahne, dört bakış

Aşağıda aynı sahne ortak: zemin ızgarası, merkezde anker küp (biraz daha büyük; yüz tonları + hafif emissive, belirgin kenar), köşede eksen okları (X/Y/Z). Dört alt kamera öğretici set: Front / Right / Top / Free (Türkçe: ön · sağ · üst · serbest orbit). Ön / sağ hafif yukarı, üst tam tepe değil (ufak X/Z offset) — blueprint düzü yerine 3B derinlik okunur. Renkli küre ve sahnedeki görüş çizgileri hangi alt kameranın ankere baktığını gösterir; köşe etiketinin üzerine gelince hem etiket hem çizgi hem de tuval kenarında viewport glow ile eşleşme güçlenir.

Üstteki şema, 2×2 Front / Right / Top / Free düzenini özetler (kavramsal). Alttaki canlı demo aynı mantıkla tuvali viewport’lara böler; köşe yazıları WebGL normalize düzlemde (0,0) sol alt, (1,1) sağ üst köşesine karşılık gelir.

Tek sahne · çoklu bakış — FPS

Tek render()ArrayCamera; her alt kamerada çizim tamponu pikseli viewport.

Görünüm sayısı
Front (Ön) Konum (0, 1.2, 4) · +Z ön · anker küp
Right (Sağ) Konum (4, 1.2, 0) · +X sağ · anker
Top (Üst) Konum (0, 5, 0.01) · neredeyse tepe, hafif tilt
Free (Serbest) Orbit · dönen bakış

Bu demo yalnızca ArrayCamera + tek render() yolunu gösterir: aynı sahne, alt kameraların her birinde tanımlı piksel viewport ile tek çağrıda çizilir. Üretimde alternatif olarak her kamera için döngüde setViewport / setScissor + ayrı render() da kullanılabilir; bu sayfa o kalıbı teoride anlatır, arayüzde ise yalnızca ArrayCamera vardır.

Görünüm sayısı 1 / 2 / 4 olarak değiştikçe hem viewport düzeni hem de aktif alt kamera sayısı güncellenir. GPU, kaç panel olursa olsun sahneyi her görünüm için yeniden rasterize eder; ArrayCamera’nın pratik kazancı çoğu projede tek çağrı ve daha sade senkronizasyon tarafındadır. İlk yüklemede tek görünüm seçilidir; 2 veya 4 paneli radyo ile açarak düzeni inceleyebilirsiniz.

Aynı karede çoklu bakış: Tek render() ile tüm alt kameralar aynı zaman damgasında işlendiği için çoklu viewport düzenleri genelde stabil ve okunaklı kalır. Hizasız küp veya ızgara çoğu zaman aspect / projeksiyon ile viewport pikselinin uyuşmamasıdır; doğrulama ve ince ayar için Son karar (doğrulama ve demo kapsamı) bölümüne bakın.

İpucu: Az sayıda viewport ve statik sahnelerde fark küçük olabilir; ancak kamera sayısı arttıkça ArrayCamera avantajı daha belirgin hale gelir. Özellikle dört panelde küp veya ızgara hizası “biraz kaymış” görünüyorsa önce viewport ile aspect / projeksiyon eşleşmesine bakın (aşağıdaki tablo, notlar ve kontrol listesi).

Bağlantı: Her köşe etiketi, aynı renkteki alt kamera küresi ve ankere giden çizgi ile eşleşir. Fareyi etiketin veya o paneldeki sahnenin üzerine getirince vurgu artar — hangi viewport’un hangi bakışa ait olduğunu bir bakışta kapatırsınız.


Üstteki etkileşimli sahne (diagram-array-camera.js, kök data-array-camera-demo) ile aynı sayıları ön harita tabloda topladık; aşağıdaki kod özeti demodaki anker, klip düzlemleri, alt kamera yerleşimi ve viewportNorm → piksel dönüşümünü hatırlatır. Ara yüzde değişenler HUD satırında; kalan satırlar dosyadaki sabitlerdir. Tür sütunu: ekran kontrolleri ile kaynak koddaki değerleri ayırır.

diagram-array-camera.js — Sayfa Demosu Sabitleri (array-camera.html playground)
Sahne / rol Parametre Değer Tür
Ortak · renderer WebGLRenderer antialias: true, alpha: false, powerPreference: "high-performance" 🔒 Sabit
Piksel oranı / renk min(devicePixelRatio, 2) · çıkış SRGBColorSpace (destekleniyorsa) · setClearColor(0x05060c) 🔒 Sabit
Ortak · sahne Scene.background 0x05060c 🔒 Sabit
Işıklar AmbientLight 0xffffff, yoğunluk 0.42 🔒 Sabit
DirectionalLight 0xffffff · 1.15; konum (4, 9, 6) 🔒 Sabit
Zemin GridHelper Boyut 16, bölüm 32; renkler 0x3a4a6a / 0x1a2030; position.y = -0.6 🔒 Sabit
Anker küp BoxGeometry / çoklu yüz Kenar 1.32; merkez yaklaşık y = 0.35 · MeshStandardMaterial (yüz başına ton + hafif emissive) 🔒 Sabit
Çerçeve + eksen EdgesGeometry · AxesHelper Kenar opaklık 0.88; eksen uzunluk 1.58 (küp ile hizalı) 🔒 Sabit
Alt kamera · ortak PerspectiveCamera FOV 40°, clip 0.0880 🔒 Sabit
Renk kodları · COLORS front 0x5ec8ff · side 0xc86bff · top 0x7dff9a · orbit 0xff9f6e 🔒 Sabit
viewportNorm → piksel · resize / updateAspects 0–1 yerleşim userData.viewportNorm; çizim tamponu canvas.width / height ile subCamera.viewport ve aspect = pw/ph 🔒 Sabit
Dört panel yerleşimi · normalize (x,y,w,h) Örn. 4 görünüm: üst sıra (0,0.5,0.5,0.5), (0.5,0.5,0.5,0.5); alt sıra (0,0,0.5,0.5), (0.5,0,0.5,0.5) — WebGL sol alt köşe 🔒 Sabit
Yardımcı + çizgi · küre · Line Küre yarıçap 0.17; çizgi opaklığı taban 0.28 (hover’da artar) 🔒 Sabit
Front / Right / Top position + lookAt(origin) (0, 1.2, 4) · (4, 1.2, 0) · (0, 5, 0.01); origin = (0, 0.35, 0) 🔒 Sabit
Free (orbit) dairesel yörünge Yarıçap 6.15; açı += dt×0.058; dikey y ≈ 1.48 + sin×0.34; lookAt(origin) 🔒 Sabit
HUD Görünüm sayısı ArrayCamera + tek render() · 1 / 2 / 4 panel 🔧 HUD
// diagram-array-camera.js — Sayfa Demosu Sabitleri (üstteki tablo ile; özet)

const COLORS = {
  front: 0x5ec8ff,
  side: 0xc86bff,
  top: 0x7dff9a,
  orbit: 0xff9f6e,
};

const NEAR = 0.08;
const FAR = 80;
const CUBE_SIZE = 1.32;
const ORIGIN = new THREE.Vector3(0, 0.35, 0); // anker (küp merkezi)

// Alt kamera şablonu: FOV 40° — viewport pikseli resize’ta userData.viewportNorm’dan
// aspect = (norm.z * bufW) / (norm.w * bufH); updateProjectionMatrix()

// Bakış konumları (lookAt → ORIGIN)
// Front (0, 1.2, 4) · Right (4, 1.2, 0) · Top (0, 5, 0.01)
// Free: orbit yarıçap 6.15; orbitAng += dt * 0.058; y = 1.48 + sin(orbitAng * 0.38) * 0.34

// ArrayCamera: new THREE.ArrayCamera(subCameras.slice(0, viewCount));
// renderer.render(scene, arrayCamera); // 1 panel: doğrudan subCameras[0]

“Sahne yok” gibi — ne zaman endişe?

ArrayCamera yolunda yalnızca köşe yazıları görünüp 3B içerik boş hissediliyorsa, bu çoğu zaman “sahne yok” değil; tek render() içinde viewport / projeksiyon / bakış vektörü zinciri tam oturmamış demektir. Aynı sahneyi hata ayıklamak için döngüde her kamera için ayrı render() denemek de yaygındır; bu demoda yalnızca ArrayCamera yolu gösterilir.

Kontrol listesi (üretimde de aynı):

  • Viewport (r170, WebGLRenderer + ArrayCamera) Alt kameranın viewport değeri bu yolda çizim tamponu pikseli olarak gl.viewport’a iletilir; 0–1 normalize bırakırsanız pratikte birkaç piksel alanına sıkışır ve sahne “yok” görünür. Bu demoda yerleşim userData.viewportNorm (0–1, sol alt orijin) olarak tutulur; resize sonrası subCamera.viewport.set(nx*bufW, ny*bufH, nw*bufW, nh*bufH) ile piksele çevrilir. Çoklu render() döngüsünde setViewport / makas ile aynı ölçeği koruyun.
  • Aspect + matris Her alt görünüm için aspect = o panelin viewport piksel eni ÷ boyu; ardından updateProjectionMatrix()atlanırsa veya viewport ile uyumsuzsa küp/ızgara “kaymış” veya sıkışmış görünür. Çoklu render() döngüsünde her iterasyonda setViewport / setScissor ile aynı w, h üzerinden hesaplayın.
  • Bakış position + lookAt (veya eşdeğeri) sahneye kilitlenmezse “boş” veya tek renk alan hissi oluşur.

Viewport, aspect ve (alternatif) döngüde çoklu render()

Dört görünümde bazı panellerde küpün hafif yan kaymış veya ızgaranın tam oturmamış görünmesi çoğu zaman “kamera kötü” değil, genişlik ÷ yükseklik ile çizim alanının uyuşmamasıdır: yani camera.aspect güncellenmeden viewport değişmiş demektir. Bu demoda resize sonrası her alt kamera için aspect = viewportGenişliği / viewportYüksekliği ve updateProjectionMatrix() uygulanır.

Çoklu render() kullanırken (genel kural): her alt görünüm için, o pass’ten hemen önce sırayla şunların tutarlı olması gerekir:

  • renderer.setViewport(x, y, w, h) ve gerekiyorsa renderer.setScissor(x, y, w, h) ile aynı dikdörtgen; setScissorTest(true) komşu bölgelere taşmayı keser.
  • camera.aspect = w / h ardından camera.updateProjectionMatrix() — atlanırsa projeksiyon yanlış en-boy oranında kalır ve sahne “kaymış” görünür.

Bu tür algı farkları eğitim ve keşif içeriğinde genelde sorun teşkil etmez; ürün veya stüdyo demosunda ise son ince ayar (tam senkron pipeline, sabit zaman adımı vb.) tercih edilebilir. Bu sayfadaki dört bakış (ön, sağ, hafif tilt üst, orbit) üretim tarafında da tutarlı, okunaklı bir set olarak düşünülebilir.

Son karar: doğrulama, ince hizalama ve demo kapsamı

Kalan fark ne? Viewport / aspect düzeltmelerinden sonra görüntü belirgin şekilde daha stabil; bariz bir hata kalmaması beklenir. Motor ve sürüme göre viewport piksel yuvarlaması veya kayan nokta hassasiyeti kaynaklı çok ince farklar görülebilir; bu tür durumlar çoğu eğitim demosunda pratik sorun teşkil etmez. Bu bir bug değildir; “piksel mükemmel” hizalama gerekiyorsa ek doğrulama ve sabit çözünürlük senaryoları düşünülür.

Sistem doğru mu? Pratik doğrulama: 1 görünümde tek panel tutarlı; 2 görünümde düzen simetrik; 4 görünümde her bakış doğru eksen / açıdan okunuyorsa, kamera dönüşümü + projeksiyon hattı tutarlı demektir. Ön bakış net ve ankere kilitli; sağ eksen okunabilir; üst tam düz değil, hafif tilt — derinlik için doğru tercih; serbest orbit dinamik bakış hissi verir. Bu set salt “tutorial” değil; ürün / eğitim içeriğinde rahatlıkla kullanılabilecek düzeyde okunaklıdır.

Ek model gerekir mi? Hayır — bu demodaki amaç zengin sahne göstermek değil, viewport + ArrayCamera render hattını okunaklı göstermektir. Fazla obje dikkati dağıtır; mevcut sade küp + ızgara + eksen bu hedef için uygun seçimdir.

İsteğe bağlı “bir üst seviye” dokunuşlar (hiçbiri zorunlu değildir): aktif viewport’ta hafif vurgu, hover’da kamera → küp çizgisinin güçlenmesi, köşe etiketlerinde kısa giriş animasyonu. Canlı demoda fareyle panel / sahne üzerinde glow ve çizgi opaklığı zaten güncellenir; ekstra animasyon tamamen tercihe bağlıdır.

Uygulama: dörtlü bölünme kurulumu

Aşağıdaki örnek, ekranı dört eşit panele böler; her alt kamera farklı bir konumdan sahneye bakar. Üretimde pencere oranı değişince her alt kameranın aspect ve viewport değerlerini güncellemeyi unutmayın. Canlı demodaki (diagram-array-camera.js) yerleşim ve hata ayıklama notları için bölüm 3’teki kontrol listesine bakın.

// diagram-array-camera.js ile aynı fikir:
// — yerleşimi 0–1 (sol alt) userData.viewportNorm’da tut
// — ArrayCamera yolunda subCamera.viewport = çizim tamponu pikseli + aspect = pw/ph

const subCameras = [];
const origin = new THREE.Vector3(0, 0.35, 0); // anker (demodaki küp merkezi)

const layoutsNorm = [
  new THREE.Vector4(0, 0.5, 0.5, 0.5),
  new THREE.Vector4(0.5, 0.5, 0.5, 0.5),
  new THREE.Vector4(0, 0, 0.5, 0.5),
  new THREE.Vector4(0.5, 0, 0.5, 0.5),
];

const positions = [
  new THREE.Vector3(0, 1.2, 4),
  new THREE.Vector3(4, 1.2, 0),
  new THREE.Vector3(0, 5, 0.01),
  new THREE.Vector3(3.5, 1.5, 3.5), // Free: demoda orbit; burada örnek sabit bakış
];

function applyViewportPixels(subCamera, norm, bufW, bufH) {
  subCamera.userData.viewportNorm = norm.clone();
  const pw = norm.z * bufW;
  const ph = norm.w * bufH;
  subCamera.aspect = pw / Math.max(1, ph);
  subCamera.updateProjectionMatrix();
  subCamera.viewport.set(norm.x * bufW, norm.y * bufH, pw, ph);
}

for (let i = 0; i < 4; i++) {
  const subCamera = new THREE.PerspectiveCamera(40, 1, 0.08, 80);
  subCamera.position.copy(positions[i]);
  subCamera.lookAt(origin);
  const canvas = renderer.domElement;
  applyViewportPixels(subCamera, layoutsNorm[i], canvas.width, canvas.height);
  subCameras.push(subCamera);
}

// Her resize / setSize sonrası: layoutsNorm[i] ile applyViewportPixels(...) tekrar

// 2. ArrayCamera
const camera = new THREE.ArrayCamera(subCameras);

// 3. Render döngüsünde tek çağrı
renderer.render(scene, camera);

Teknik tablo: ArrayCamera avantajları

Özellik Geleneksel çoklu render ArrayCamera yaklaşımı
GPU / çağrı düzeni Her görünüm için ayrı render döngüsü veya manuel viewport yönetimi Tek render() içinde çoklu görünüm; viewport atamaları çerçevelenmiş akış
CPU / JS gecikmesi Döngüde birden çok .render() — senkronizasyon zorlaşabilir Tek çağrı; tüm alt görünümler aynı kare zamanında planlanır
Senkronizasyon Kareler arasında mikro kaymalar olabilir Aynı sahne anı; bakışlar tutarlı güncellenir
Kod karmaşıklığı Manuel viewport, makaslama ve temizleme sırası Alt kameralara viewport bağlama; ArrayCamera ile toplu kullanım
GPU yükü (netleştirme) Her alt görünüm için geometri ve piksel işi yapılır — yani sahne karmaşıklığı görünüm sayısıyla çarpılır. Kazanç, çoğunlukla tek render pipeline / tek çağrı düzeni ve daha iyi senkronizasyondadır; “bedava dört kamera” değildir.

Post-processing ve tam ekran geçişler

Hazır post zincirleri (ör. bloom, SSAO, TAA, tonemap) çoğu zaman tek tam ekran dikdörtgeni ve tek kamera projeksiyonu üzerinden tasarlanır. ArrayCamera ile birlikte kullanmak, bu geçişleri her alt görünüm için ayrı hedef / ayrı rect düşünmeyi gerektirir; aksi halde efekt yanlış bölgeye uygulanır veya birleştirme (kompozit) adımı eksik kalır. Yoğun post için çoklu bakışı genelde ikinci bir tampon, düşük çözünürlüklü ortak geçiş veya son kompozitör katmanıyla planlamak gerekir — bu yüzden bölüm 6’daki “post-processing zorlaşır” uyarısı pratikte “pipeline’ı baştan bölersiniz” anlamına gelir.

Döngülü render() ve clear / autoClear

Bu sayfadaki canlı demo yalnızca ArrayCamera yolunu gösterir; üretimde ise klasik desen olarak her bakış için setViewport ve çoğu zaman setScissorTest(true) ile döngüde birden fazla render() yazılabilir. Bu kalıpta hangi adımda renk veya derinlik temizlediğiniz doğrudan sonucu belirler: örneğin kare başında bir kez tam ekran clear, sonraki panellerde yalnızca clearDepth; veya ara geçişlerde autoClear’i kapatıp yalnızca ihtiyaç duyulan bölgeleri temizlemek. Sıra, makas ve viewport hizası; bir panelde “sahne yok” veya garip üst üste binme hissinin sık nedenlerindendir.

Sınırlar, ne zaman kullanılmaz ve dikkat

ArrayCamera kullanışlıdır; yine de sınırları vardır:

  • Homojen türler: Dizideki kameraların çoğu senaryoda aynı türde (ör. hepsi PerspectiveCamera) olması beklenir; karışık türler özel durumlarda ek dikkat ister.
  • Dinamik boyutlandırma: Pencere resize olduğunda her alt kameranın aspect ve viewport değerlerini döngüyle güncellemek gerekir.
  • Gölge ve ışık: Işık hesapları sahne için ortaktır; çok sayıda bakış ve gölge haritası yine de maliyeti artırır — alt kamera sayısı fazlaysa profil çıkarın.

Ne zaman (genelde) tercih etmeyin?

  • Tek bakış yeterliyse: ek karmaşıklık ve test yükü gereksiz olur.
  • Mobil / düşük güç: çok viewport, küçük ekranda hem okunabilirlik hem GPU maliyeti sorun çıkarabilir.
  • Yoğun post-processing zinciri: her alt görünüm için ayrı geçiş gerektiren efektler ArrayCamera ile zorlaşır; efektler çoğu zaman fullscreen pass varsaydığı için ya her panel için ayrı hedef (RT) ve kompozit gerekir ya da önce ortak tamponda birleştirip sonra tek geçişte işlenir.

Holodepth notu: neden viewport?

Varsayılan olarak renderer tüm tuvali boyar. ArrayCamera kullanırken her alt kameraya bir çizim alanı (viewport) vermezseniz, görünümler üst üste binebilir veya yalnızca son ayar öne çıkabilir. Viewport, GPU’ya şunu söyler: “Bu kameranın çıktısını tuvale yalnızca şu koordinatlar arasına yaz.” — split-screen ve çoklu monitör düzeninin teknik karşılığı budur.

Pratik hata ayıklama için bölüm 3’teki “Sahne yok” kontrol listesine bakın; özellikle normalize mi piksel mi ve aspect güncellemesi çoğu “boş panel” vakasını çözer.