holodepth

Three.js · Geometri

Morph targets (şekil değiştirme ve mimikler)

Kemiksiz deformasyon: vertex’ler hedefe kayar

Morph targets, bir geometrinin temel yapısını (base) koruyarak, vertex’lerinin alternatif konumlarını saklama yöntemidir. Diğer animasyon türlerinden farkı, kemiklere (rigging) ihtiyaç duymadan doğrudan noktaların yer değiştirmesiyle çalışmasıdır. GPU, temel pozisyon ile hedef pozisyon arasında matematiksel bir köprü kurarak akıcı bir geçiş (interpolation) yaratır. Aynı topolojide “tek mesh, çok kopya” performansı için Instancing sayfasına; burada ise tek bir geometrinin şekil varyantları üzerinde duruyoruz — ikisi farklı problemlerdir.

Çerçeve için Geometri Giriş; vertex verisi için Attributes & buffer’lar; zaman için Animasyon Döngüsü & Zaman.

Çalışma mantığı: deformasyonun matematiği

Bir küpün bir küreye dönüştüğünü hayal edin. Küpün her bir köşesi (vertex), küre üzerindeki hedef konumuna doğru bir yol izler.

  • Base state: Nesnenin orijinal hali.
  • Target state: Nesnenin ulaşması gereken son hali.
  • Influence (etki): 0 ile 1 arasında bir değerdir. 0 ise orijinal hali, 1 ise tam hedef hali gösterir.

Çoğu örnekte konu pozisyon morph’udur; oysa hedef, yalnızca konum değil başka öznitelikler için de tutulabilir. BufferGeometry üzerinde morphAttributes altında örneğin morphAttributes.position, morphAttributes.normal ve gerektiğinde morphAttributes.tangent (normal haritalı yüzeylerde) görülebilir. Işık ve gölgelerin bozulmaması için özellikle belirgin şekil değişimlerinde normal morph çoğu zaman kritiktir.

Shader tarafında sezgi aynıdır: her hedef ve etki için, kabaca finalPosition = mix(base, target, influence) (GLSL’deki mix) ile taban ve hedef arasında ara değer üretilir; normal/tangent morph’ları da aynı mantıkla kendi kanallarında karıştırılır.

Birden fazla hedef aynı anda etkiliyse, Three.js materyalleri bunları tipik olarak üst üste karıştırır; her morphTargetInfluences[i] değeri kendi hedefine giden ağırlık gibi düşünülmelidir. Pratikte çoğu yüz rig’inde etkileri 0–1 aralığında tutmak ve toplam “aşırı şişme”yi gözlemlemek, artefakt riskini azaltır.

Kullanım alanları: neden morph targets?

Morph, bir ürün konfigüratöründen canlı mimik animasyonuna kadar geniş skalada iş görür; asıl kısıt çoğu zaman üretim disiplini (hedef sayısı, isimlendirme, normal/tangent eşliği) ve GPU bellek bütçesidir.

  • Yüz animasyonları: Konuşma (lip-sync), göz kırpma, gülümseme gibi mikro hareketler için çok verimli bir yol.
  • Karakter özelleştirme: Boy, burun, kas gibi slider mantığıyla kontrol edilen şekil varyasyonları.
  • Basit akışkanlar: Kemik sisteminin ağır kalacağı hafif dalgalanmalar.
  • Düzeltici (corrective) şekiller: Belirli pozlarda rig artefaktını gidermek için kullanılan hedefler — oyun ve film boru hatlarında sık görülür.

Teknik tablo: morph targets vs. skeletal animasyon

Özellik Morph targets Skeletal (kemik) animasyon
Kontrol birimi Her bir vertex (nokta) Kemikler ve eklemler
Esneklik Organik ve hacimsel değişimler için güçlü Eklem yerlerindeki bükülmeler için ideal
Performans GPU üzerinde çok hızlı olabilir CPU/GPU maliyeti senaryoya göre artabilir
Hazırlık Modellemede blend shapes / shape keys Rigging ve weight paint

Uygulama: kod ile şekil değiştirme

Three.js’te bir modelin morph hedeflerine erişmek ve onları kontrol etmek genelde morphTargetInfluences üzerinden yapılır. glTF gibi formatlarda hedefler çoğu zaman isimle gelir; indeks yerine isim kullanmak büyüyen projelerde daha güvenlidir.

// 1) Modelin (Mesh) içindeki morph hedeflerini kontrol et
// Genellikle Blender'da hazırlanan modellerde isimlerle (smile, blink vb.) gelirler.
const mesh = gltf.scene.children[0];

// 2) Bir hedefi aktifleştir (Örn: Gülümseme etkisini %100 yap)
mesh.morphTargetInfluences[0] = 1.0;

// 3) Animasyon döngüsünde dinamik olarak değiştir
function animate() {
    requestAnimationFrame(animate);

    // Zamanla değişen bir etki (sinüs ile yumuşak geçiş)
    const influence = Math.sin(Date.now() * 0.005) * 0.5 + 0.5;
    mesh.morphTargetInfluences[0] = influence;

    renderer.render(scene, camera);
}

İndeks yerine isim kullanmak için önce sözlükten indeks alın; böylece DCC tarafında hedef sırası değişse bile kod stabil kalır:

const dict = mesh.morphTargetDictionary; // { smile: 0, blink: 1, ... }
const influences = mesh.morphTargetInfluences;

function setMorphByName(name, value) {
    const i = dict[name];
    if (i === undefined) {
        console.warn('Morph yok:', name);
        return;
    }
    influences[i] = THREE.MathUtils.clamp(value, 0, 1);
}

setMorphByName('smile', 0.35);

glTF animasyonları morph ağırlıklarını bazen AnimationMixer üzerinden sürer; aynı karede hem mixer track’i hem de el ile morphTargetInfluences yazmak çifte kontrol riski doğurur. Kural: bir kaynak seçin (mixer veya manuel tween) — detaylı zaman eğrileri için Lerp, Easing & Animasyon Mixer sayfasına bakın.

Optimizasyon: morph target bütçesi

Her bir morph hedefi, geometrinin vertex pozisyon verisini pratikte “kopya bir katman” gibi taşır. Bu yüzden:

  • Bellek yükü: Çok sayıda hedef, vertex sayısıyla çarpılarak büyür.
  • Sınır: Eski cihazlarda aynı anda aktif olabilecek hedef sayısı sınırlı olabilir.
  • Pratik kural: Etkisi 0 olan hedefleri gereksiz yere sürekli “açık” tutmayın; pipeline’ınızda hedefleri azaltın veya LOD düşünün.

Ham veri tarafında hedefler, BufferGeometry.morphAttributes altında tutulur; vertex başına byte hesabı için Attributes & buffer’lar sayfasındaki bellek cetveli mantığını hatırlayın — her hedef, seçtiğiniz kanallarda (pozisyon, normal, tangent) ek bir katman gibidir.

WebGL sürücüleri ve donanım profiline göre morph ile ilişkilendirilebilecek vertex attribute yolu sınırlı olabilir; mobilde “çok hedef + çok yüksek vertex” kombinasyonu düşük FPS veya dokuşmuş görünüm üretebilir. Şüphedeyseniz hedefleri azaltın, gereksiz normal morph’larını birleştirin, sahnede gerçek cihaz profiliyle ölçün.

Three.js API: sözlük, etkiler ve geometri tutarlılığı

Çalışma zamanında en sık dokunduğunuz iki alan morphTargetInfluences (sayı dizisi) ve morphTargetDictionary (isim → indeks) ikilisidir. Geometri yüklenirken hedef sırası export ayarına göre değişebileceği için, üretim kodunda mümkün olduğunca isim üzerinden gitmek en az sürtünmeli yoldur.

  • Topoloji eşleşmesi: Her morph hedefi, taban geometri ile aynı vertex sayısı ve yüz örgüsüne sahip olmalı; aksi durumda yükleme veya deformasyon hatalıdır.
  • Normal güncelleme: Dış kaynaktan ham pozisyon morph’u enjekte ediyorsanız, ışık kalitesi için geometride normal morph’u da taşıdığınızdan emin olun; gerekirse DCC tarafında yeniden üretin.
  • mesh.updateMorphTargets(): Geometriye sonradan morph attribute’ları eklediyseniz veya runtime’da değiştirdiyseniz, mesh’in hedef listesini yeniden senkronlamak için bu çağrı gerekebilir.

Özel materyal / ShaderMaterial yazıyorsanız, standart mesh materyallerinin sağladığı morph birleşimini kendiniz üstlenmeniz gerekir; bu sayfanın matematik bölümündeki mix sezgisi burada da başlangıç noktasıdır.

Holodepth notu: Blender ve isimlendirme

İsimle eriş: indeksler değişir, isimler kalır

Blender’da verdiğin Shape Key isimleri Three.js tarafında morphTargetDictionary içinde anahtar olarak görünür. Kod yazarken morphTargetInfluences[0] yerine mesh.morphTargetDictionary['smile'] üzerinden indeks bulup o indeksi kullanmak, proje büyüdüğünde hata yapmanı engeller.

glTF dışa aktarımda isimlerin korunması için Blender tarafında gereksiz boşluk / özel karakter kullanmaktan kaçının; bazı araç zincirlerinde anahtarlar normalize edilebilir. Export sonrası tarayıcı konsolunda morphTargetDictionary’yi bir kez yazdırıp “beklediğim isimler geldi mi?” kontrolü ucuz ve etkili bir doğrulamadır.