[Devlog] Modern Clustered Forward Aydınlatma: Emil Persson Modeli & 3-Geçişli GPU Compute Bitmask Ayıklama
Ashenfall grafik hattında, lokal ışıklandırma için kullanılan ilkel yöntemleri tamamen rafa kaldırdım. Sahneye birkaç meşale veya büyü efekti eklendiğinde GPU çizim çağrılarını (draw call) patlatan, geometriyi ışık başına tekrar tekrar çizen 2000'ler nesli çoklu geçişli (multi-pass forward) mimariyi projeden kazıdım.
Yerine, modern AAA yapımların (Just Cause / Avalanche Engine) temelini oluşturan
Emil Persson (SIGGRAPH 2015) Clustered Shading mimarisini Direct3D 11 motoruma entegre ettim.
Eski Yöntem Neydi ve Neden Terk Edildi?
Geleneksel ileri aydınlatmada (Forward Shading), sahnedeki her dinamik ışık için ya objeler tekrar tekrar sahneye çizilir (draw call çöküşü) ya da her piksel sahnede var olan tüm ışıkları tek tek test eden devasa döngülere girerdi (O(Piksel × Işık)). Bu durum, ekranda aynı anda 10–15'ten fazla dinamik ışık bulunmasını teknik olarak imkansız kılıyordu. Deferring mimarisi ise MSAA uyumsuzluğu ve devasa G-Buffer bellek bant genişliği maliyetleriyle küçük ölçekli sistemleri kilitliyordu.
Yeni Sistem Nasıl Çalışıyor? (Matematiksel ve Mimari Altyapı)
Yeni mimariyle birlikte sahne geometrisi
yalnızca bir kez (Single-Pass Forward) çiziliyor; pikseller sadece kendilerini gerçekten aydınlatan ışıkların listesini doğrudan bellekten okuyor.
- 64x32 Logaritmik Hiyerarşik Hacim Izgarası (Clustered Frustum Grid):
Ekran alanı 64×64 piksellik iki boyutlu karolara (tiles), kamera derinliği ise 0.1 m ile 200 m arasında 32 adet logaritmik derinlik dilimine (depth slices) bölünüyor. Yakın mesafelerde dilimler dar tutularak derinlik hassasiyeti korunuyor, ufka doğru logaritmik olarak genişletilerek binlerce 3B kübik hücre (cluster) oluşturuluyor.
- 3-Geçişli GPU Compute Bitmask Ayıklama (3-Pass Compute Culling):
CPU'nun ışıkları tek tek test edip sahne ayıklaması devri kapandı. Culling hattı GPU Compute Shader (cs_5_0) üzerine 3 paralel geçişle paylaştırıldı:
- Pass 1 (Bounds Reduction): 1024 ışığın her birinin 32 derinlik dilimindeki min/max ekran izdüşümleri çıkarılıyor.
- Pass 2 (Row/Column Bitmasks): Ekran karolarının X ve Y eksenlerinde ışıkların kesişim bitmask'leri hesaplanıyor.
- Pass 3 (Assign & Tile-Sharing): Bitwise AND operasyonları ile hücreler taranıyor, ışık indisleri ekran karosu başına ayrılmış dinamik indeks tamponuna (g_rwIndices) ve ızgaraya (g_rwGrid) yazılıyor.
- Ters Çözümleme (Analitik Cluster Tespiti):
İleri aydınlatma piksel shader'ında hiçbir arama algoritması veya bounding box testi çalışmıyor. Pikselin derinliğinden ve ekran koordinatından doğrudan analitik bir formülle (log2 derinlik ölçeklemesiyle) o pikselin ait olduğu kübik cluster hücresi anında tespit ediliyor (O(1) lookup).
- Fiziksel Mesafe Sönümlemesi (Quadratic Energy Conservation):
Işıklar, lineer ve karesel enerji korunum formülüyle 1 / (d² + 1) olarak hesaplanıyor; maksimum etki menziline (fRange) ulaştığında ise analitik pencere fonksiyonuyla sıfıra yumuşatılarak karolar arası dikiş izi (seam) oluşumu engelleniyor.
Performans & Optimizasyon
Yüzlerce ışığın sahneye girdiği yoğun senaryolarda mimarinin sağladığı kazanımlar:
- 1024 Dinamik Işık Kapasitesi: Sahneye serpiştirilen yüzlerce hareketli ve renkli nokta/spot ışık altında bile çizim çağrısı (draw call) artışı veya CPU darboğazı tamamen sıfırlandı.
- Hafif Bellek Ayak İzi: Grid, maske ve indeks tamponlarının VRAM üzerindeki toplam yükü yalnızca ~400–600 KB seviyesindedir.
- Düşük Hesaplama Maliyeti: 1024 ışık senaryosunda tüm culling ve aydınlatma geçişinin 1080p çözünürlükteki toplam GPU maliyeti ~0.3–0.4 ms aralığında kilitli kalmaktadır.
Köy meydanına yerleştirilen yüzlerce dinamik ışığın araziyle pürüzsüz harmanlandığı stres testi karesini yukarıda inceleyebilirsiniz.