Three.js · Çoklu model · Sahne birleştirme · Runtime
Çoklu model ve sahne birleştirme: Runtime yönetimi
Bir 3D sahne, çoğu zaman tek bir .glb dosyasından ibaret değildir. Karakterler, çevre öğeleri, silahlar ve araçlar farklı kaynaklardan gelir. Scene graph (sahne grafiği), bu bağımsız parçaların birbirine nasıl «eklemleneceğini» belirleyen motor odasıdır.
Bu sayfa, dosyayı yükledikten sonraki runtime aşamasına odaklanır: düğümleri birleştirmek, güvenli şekilde ayırmak, dönüşümün çocuklara yayılmasını anlamak ve glTF pipeline ile uyumlu «kanca» noktaları kurmak.
glTF içi scene graph konusu, içe aktarılmış dosyanın ağacını okumayı merkeze alır; burada ise aynı kurallarla birden fazla kaynaktan gelen modelleri çalışma anında bir araya getirmeyi anlatıyoruz — yani «grafik nedir?» yerine «grafikle oynayıp sahneyi nasıl kurarım?» sorusu.
Önce çekirdek kavramlar için Sahne — scene graph ve Object3D hiyerarşisi sayfalarına göz atın.
Düğüm birleştirme (merging) ve ayrıştırma
Statik bir dosyayı sahneye yüklediğinizde, hiyerarşi dosyadaki haliyle «donmuş» gelir. Oysa interaktif bir deneyimde bu ağacı canlı olarak yeniden düzenlemeniz gerekir: bir parçayı başka bir modelin altına taşımak, geçici olarak sahneye çıkarmak veya tak-çıkar ekipman akışı kurmak gibi.
Birleştirme sırasında sıra önemlidir: önce hedef ebeveynin (transform’unun)
mantıklı olduğundan emin olun, sonra çocuğu taşıyın. Gerekirse taşıma sonrası
child.updateMatrixWorld(true) ile alt ağacın dünya matrislerini tazeleyin;
böylece
fizik gövdeleri veya raycast hemen doğru uzayda çalışır.
Bağımsız model ekleme
İki farklı glTF sahnesini birleştirirken, bir modelin belirli bir
düğümünü (node / Object3D) alıp diğerinin
altına
taşıyabilirsiniz. Pratikte sık görülen kalıp: kök veya alt grup üzerinde
parent.add(child) ile hiyerarşiyi yeniden kurmak.
Aynı anda birden fazla yükleme yapıyorsanız, her kökü geçici bir THREE.Group
altında
toplayıp sahneye tek seferde eklemek; sonra alt düğümleri hedef hiyerarşiye dağıtmak hata
ayıklamayı
kolaylaştırır — hangi paketin hangi dalı taşıdığı net kalır.
Detach — dünya konumunu korumak
Bir objeyi ebeveyninden ayırdığınızda, ekranda aynı yerde kalması için
scene.attach(object) kullanın. Bu yöntem, nesneyi mevcut ebeveyninden
çıkarırken
dünya matrisini korur; yalnızca remove + yeniden
add ile bazen beklenmedik sıçramalar oluşabilir. Detaylı düğüm yönetimi için
Node hiyerarşisi konusuna bakın.
scene.attach, nesneyi doğrudan sahnenin çocuğu yapar ve yerel transform’u dünya
uzayına
göre yeniden yazar. Yeni bir ebeveyne (ör. başka bir karakterin eline) tekrar bağlamadan
önce
gerekirse yerel eksenleri sıfırlamak için kısa bir «pivot düzeltme» adımı eklemeniz
normaldir.
Transform propagation (dönüşüm yayılımı)
Hiyerarşinin kalbi, ebeveyndeki bir değişikliğin çocuklara nasıl aktığıdır: çocuğun yerel (local) transform’ı sabit kalsa bile, zincirlenen matris çarpımıyla dünya uzayında yeni konuma taşınır.
Matematiksel zincir: bir çocuğun dünya matrisi, kökten kendisine kadar tüm
üst düğümlerin çarpımıdır
M_world = M_root × M_parent × M_local.
Varsayılan olarak matrixAutoUpdate açıktır; ebeveyn hareket ettikçe çocukların
matrixWorld değerleri güncellenir. Özel animasyon veya IK
sistemlerinde düğümleri elle iterken, bir karede tutarsızlık görürseniz önce bu zincirin
güncellenip güncellenmediğini kontrol edin.
Canlı etki
Örnek: araba gövdesi (parent) 5 birim ileri
gittiğinde, tekerlekler (child) yerel konumda (0, 0, 0)
civarında kalsa bile dünya konumları otomatik güncellenir. Bu davranış
transform propagation sayesindedir; motor her karede
(veya matrixWorldNeedsUpdate akışında) bu zinciri hesaplar.
Ölçek (scale) de zincire girer: ebeveyn üzerinde küçültme varsa çocuğun dünya ölçeği de çarpılarak değişir. Bu yüzden «tekerlek neden inceldi?» gibi görsel hatalarda önce üst gruplarda beklenmedik scale birikimi aranır.
glTF pipeline ile bağlantı (runtime stratejisi)
glTF dosyanızı hazırlarken (pipeline aşamasında), sahne grafiğini nasıl kurguladığınız runtime performansını ve kod sadeliğini doğrudan etkiler.
İsim tabanlı arama (getObjectByName) sözleşmesi, tasarım–kod arasında sözleşme
gibidir: isimleri değiştirmek CI kırılması yaratır; bu yüzden kritik
kancaları belgeleyin ve mümkünse tek bir «kanca öneki» (SOCKET_ gibi) kullanın.
Boş düğüm (empty) stratejisi
Blender veya dışa aktarma hattında «kanca» noktaları oluşturun: örneğin karakterin elinde
silah
tutacağı yeri boş bir THREE.Object3D / grup düğümü olarak işaretleyin. İsimleri
anlamlı verin (weapon_socket gibi).
Boş düğüm geometri taşımaz; yalnızca dönüşüm ve hiyerarşi için vardır. Bu sayede silah modeli değişse bile el animasyonu aynı kanca üzerinden bağlanır — asset sürümü değiştiğinde kodun tek dokunduğu yer genelde bu düğüm olur.
Runtime eşleşmesi
Kodda model.getObjectByName('weapon_socket') ile bu düğümü bulun; yeni yüklenen
silah modelini doğrudan bu düğümün altına add() edin. Silah, el ve parmak
animasyonlarıyla aynı hiyerarşi zincirinde hareket eder.
Aynı isimden iki düğüm kalmamasına dikkat edin; çakışmada getObjectByName ilk
eşleşeni
döndürür. Karmaşık sahnelerde traverse ile filtreleyip kullanıcı verisiyle
eşleştirmek
veya yükleme sonrası isimleri kodda yeniden adlandırmak da yaygın bir güvenlik önlemidir.
Aşağıdaki lab, aynı fikri «tech demo» estetiğinde gösterir: ortada yüzen
modüler hub (cam çekirdek + metal gövde), yanda sabit sensör
halkaları; ana parça ise prosedürel enerji modülü (bıçak + emissive
çekirdek).
weapon_socket yine boş bir gruptur; modül URL ile
çekilmez.
Attach to hub hedefe ease-out ile yaklaşıp
socket.attach ile kilitlenir; Detach (world keep)
yalnızca scene.attach + hafif drift.
Hub, sensörler ve enerji modülü yalnızca bu sayfada Three.js ile üretilir; harici .glb veya ağ adresi yok. RoomEnvironment ile IBL (metal / cam yansıması) kullanılır.
Ne izliyorsunuz?
Ortada hafifçe yüzen ve dönen modüler hub; yüzeyde
weapon_socket adlı boş bir kanca.
Add module enerji modülünü (metal gövde + üçgen bıçak + emissive
çekirdek) sahneye koyar.
Attach to hub modülü dünya uzayında hedefe doğru
ease-out ile taşır, son karede socket.attach ile
kancaya bağlar — modül hub ile birlikte döner; bağlanırken parıltı (emissive) güçlenir.
Detach (world keep) yalnızca scene.attach: dünya
matrisi korunur, ardından çok kısa bir drift.
Remove module geometriyi dispose eder.
Performans etkisi: derinlik vs. genişlik
Sahne grafiğinizin şekli, Three.js’in her karede güncellediği matris zincirinin uzunluğunu etkiler.
Bu başlık, GPU draw call sayısından farklı bir eksendir: çoğu zaman derinlik «daha pahalı matris güncellemesi» demektir; fakat görünürlük külçesi ve mantıksal gruplama maliyeti yassı ağaçta farklı şekilde ödenir. Amaç, projeniz için okunabilir ve ölçülebilir bir denge bulmaktır.
- Derin ağaç: Üst üste 10–20 katmanlı ebeveyn–çocuk ilişkisi, Mworld için daha uzun çarpım zinciri demektir; CPU tarafında ek yük oluşturabilir.
- Geniş / yassı ağaç: Çok nesneyi doğrudan sahneye bağlamak matris güncellemelerini bazen sadeleştirir; oysa toplu taşıma, seçim ve mantıksal gruplama zorlaşır.
- Instancing ile ilişki: Aynı geometriden çok kopya çiziyorsanız hiyerarşi yerine InstancedMesh gibi yollar grafik yükünü farklı kanaldan çözer; bu sayfa hiyerarşi odaklı kaldığı için ikisini birbirinin yerine koymayın — birlikte planlayın.
HoloDepth önerisi
Birlikte hareket etmesi gerekmeyen statik objeleri gereksiz yere derin hiyerarşiye bağlamayın. Yalnızca fiziksel olarak birbirine bağımlı parçalar (kapı–menteşe, tekerlek–aks gibi) için anlamlı grup yapısı kullanın.
Transform propagation — canlı izleme
Bir objenin o anki gerçek yerini anlamak için yalnızca position alanına bakmak
yanıltıcıdır; bu değer ebeveyne göre yerel ötelemeyi gösterir.
Yönelim için benzer kural geçerlir: quaternion / rotation
yereldir;
dünya uzayındaki yön için getWorldQuaternion kullanılır. Fizik motorlarına veya
debug çizimlerine beslerken çoğu zaman dünya uzayındaki temsil
doğrudur.
// Yanlış: yalnızca ebeveyne göre yerel mesafe
console.log(wheel.position);
// Doğru: sahne grafiği zincirini hesaplar, dünya konumunu döner
const worldPos = new THREE.Vector3();
wheel.getWorldPosition(worldPos);
console.log(worldPos);
HoloDepth özeti: runtime birleştirme API’leri
Aşağıdaki tablo, bu sayfadaki tekrarlayan dört işlemi tek satırda hatırlatır. glTF dosyası içindeki düğüm ağacını okumak için model içi grafik rehberine; düğüm adları ve içe aktarma sonrası yapı için Node hiyerarşisi sayfasına dönün.
İşlem → metot → amaç
| İşlem | Metot | Amaç |
|---|---|---|
| Grup oluşturma | new THREE.Group() |
Birden fazla modeli tek mantıksal konteynerde toplamak. |
| Dinamik bağlama | parent.add(child) |
Bir modeli (ör. silah) diğerine (ör. el) hiyerarşik olarak bağlamak. |
| Dünya konumu korumalı ayırma | scene.attach(object) |
Objeyi gruptan çıkarırken ekrandaki yerini kaydırmamak. |
| Hiyerarşik tarama | model.traverse() |
Tüm alt düğümlere (mesh, ışık vb.) tek geçişte ulaşıp işlem yapmak. |