← Bloga geri dön

Bloom nerede duruyor: tasarım tercihleri, ilerleme ve sıradaki adımlar

Bloom bu hafta beş haftalık oldu. Üç yüz on bir commit sonrasında, elimizde ne olduğunu, neden bu şekilde yaptığımızı ve hangi parçaların hâlâ pürüzlü kaldığını yazmak istedik.

Tek cümleyle pitch

Oyununu TypeScript ile yaz, önceden derle ve macOS, Windows, Linux, iOS ve tvOS üzerinde gerçek bir native ikili — ya da web için bir WASM paketi — yayınla. Electron yok, WebView yok, gönderdiğin oyunda gömülü JavaScript çalışma zamanı yok.

Bu cümle çok iş gördü. Erken tasarım kararlarımızın çoğunun böyle görünmesinin nedeni de bu.

Neden TypeScript

TypeScript'i JavaScript'i sevdiğimiz için seçmedik. En çok kullanılan, yapısal tip sistemine sahip, devasa bir araç ekosistemi olan ve kimseyi korkutmayan bir sözdizimi sunan, statik tipli dil olduğu için seçtik. Birlikte oyun yapmak istediğimiz insanların çoğu daha önce TypeScript yayınladı. Çok azı C++ yayınladı.

Ayrıca, gönderdiğin her ikiliye bir çöp toplayıcı ve bayt kodu yorumlayıcısı sürüklemeden önceden temiz bir şekilde derlenebilen bir dil istedik. Bu, ağır bir çalışma zamanı gerektiren her şeyi (Python, C#, dil olarak JS) eledi. TypeScript — varsayılan olarak dinamik kısımları çıkarıldığında — tatlı bir nokta olduğu ortaya çıktı.

Neden Perry

Perry, TypeScript kodunu native koda çeviren önceden derleyicidir. TS'den ikiliye giden yol, “çalışma zamanı yükü yok” sözünü vermemizi sağlayan parçadır. Oyunun, kararlı bir C ABI üzerinden bir Rust çekirdeğine çağrı yapan tek bir ikili dosyaya dönüşür. V8 yok, Bun yok, JIT yok.

Perry'yi seçmek, açığa çıkardığımız dil yüzeyi konusunda acımasız olmamıza izin verdi. Bloom API'si fonksiyonlar ve düz arayüzlerden oluşur — sınıf yok, dekoratör yok, proxy yok, eval yok. Bir TypeScript özelliği native bir çağrıya temiz bir şekilde derlenmiyorsa, onu API'de kullanmıyoruz. Sonuç, bir kopya kağıdına sığan bir API ve kafanın içine sığan bir build.

Neden wgpu, dört özel render motoru değil

İlk içgüdümüz önce bir Metal render motoru, sonra bir DirectX 12 render motoru, sonra bir Vulkan render motoru yazmaktı. Bir hafta sonu içinde kendimizi vazgeçirdik. Dört arka uç dört shader dili, dört kaynak modeli, dört hata yüzeyi ve HiDPI'ı unutmak için dört yer demektir.

Bunun yerine, render motorunun tamamı bir kez wgpu üzerine yazıldı. Shader'lar WGSL. Apple platformlarında Metal, Windows'ta DirectX 12, Linux ve Android'de Vulkan ve tarayıcıda WebGPU (WebGL yedeği ile) — tek bir kod tabanından alıyoruz. Bedeli, wgpu'nun özellik setine bağlı kalmamız ve birkaç egzotik şeyin (mesh shader'lar, ray tracing) şimdilik masada olmaması. Küçük bir ekip için bunun adil bir takas olduğunu düşünüyoruz.

Bugün gerçekten kutuda olanlar

Pazarlama sitesine çalışmayan hiçbir şeyi koymamaya çalışıyoruz. Bugün gerçek olan şu:

  • Dokuz içe aktarılabilir modülbloom/core, bloom/shapes, bloom/textures, bloom/text, bloom/audio, bloom/models, bloom/math, bloom/physics ve bloom/scene.
  • Gerçek bir PBR render motoru — substrate tarzı katmanlı materyaller, statik geometri için önbellekli kademeli gölge haritaları, ACES/AgX ton eşleme, otomatik pozlama, bloom, alan derinliği, hareket bulanıklığı, SSGI, SSAO, TAA ve kesirli render ölçekleri için bir CAS keskinleştirme geçişi.
  • GPU iskelet animasyonu — glTF 2.0 içe aktarımı, GPU üzerinde dört kemikli doğrusal karışım skinning, iskelet başına 128'e kadar eklem.
  • Jolt fizik — katı ve yumuşak cisimler, karakter denetleyicileri, araçlar, ışın izleri, kısıtlar, temas geri çağrımları. Web hedefi JoltPhysics.js yedeğini kullanır, böylece aynı kod tarayıcıda çalışır.
  • Altı hedef platform — macOS, Windows, Linux, iOS, tvOS ve Web. Android kısmen bağlandı ama henüz yayınlanabilir değil.
  • Hot reload — bir WGSL shader'ı veya bir materyal JSON'u kaydet, değişiklik geliştirme sırasında bir saniyeden kısa sürede ekranda. Dosya izleme kodu sürüm yapılarından çıkarılır.
  • On yedi örnek proje — 170 satırlık bir Pong'dan Intel Sponza ve Bistro sahnelerini yüklemeye kadar.

Yakın zamanda gurur duyduğumuz birkaç şey

Render motoru iyi bir ay geçirdi. Birkaç öne çıkan:

  • Otomatik DRS. Render motoru hedef kare hızına ulaşmak için render ölçeğini kendi kendine ayarlar, ardından görüntü net kalsın diye bir upscale artı RCAS keskinleştirme geçişi çalıştırır. Sen bir hedef FPS belirlersin; gerisini motor halleder.
  • Düzlemsel yansımalar. Eğik kırpma ile gerçek ayna düzlemi yakalamaları ve yansıma bütçesi tükendiğinde IBL yedeği. Su, parlak zeminler ve dükkân vitrinleri için kullanışlı.
  • Doku dizisi splat eşlemesi. Arazi ve detay katmanları artık uygun mip'lerle bir doku dizisi bağlayabilir, böylece katman başına bir çizim çağrısı ödemeden her döşemeye birkaç materyal boyayabilirsin.
  • Imposter baker. Uzak LOD'lar için oktahedral imposter atlasları pişiren küçük bir CLI. Sahnelere orman düşürmeye başladığımız anda buna ihtiyacımız oldu.
  • Platformlar arası HiDPI. Windows, Linux ve Web nihayet macOS ve iOS'un zaten sahip olduğu aynı HiDPI işlemesini paylaşıyor. UI her ekranda net kalır.

Bittiğini iddia etmediğimiz şeyler

Sana yağcı olmayan listeyi de borçluyuz:

  • Android. Platform crate'i mevcut ve FFI yüzeyinin çoğu bağlandı, ancak native-activity yapıştırıcısı bitmedi. Henüz kimseye Bloom ile Android yayınlamasını söylemiyoruz.
  • watchOS. Shader'lar derleniyor, platform stub'ı orada, ancak bu gerçek olmadan önce Perry'nin watchOS desteğine takılıyız.
  • Sanallaştırılmış geometri. Nanite eşdeğeri yok, mesh shader yok, donanım ray tracing yok. Bu yol haritasında, ikilide değil.
  • İskelet animasyonu sınırı. UBO boyutu nedeniyle iskelet başına 128 eklem, sıkı sınır. Büyük rig'ler bugün geçici çözümlere ihtiyaç duyuyor.
  • Sahne grafiği genç. Dönüşümler, görünürlük, gölgeler ve materyal bağlama hepsi çalışıyor. Sorgu sistemleri ve daha geniş optimizasyon geçişleri henüz yok.
  • Kullanıcı shader'ları. WGSL'i elle yazıp materyal hattı üzerinden yükleyebilirsin, ancak motor içi shader graph yok ve TypeScript'ten çalışma zamanı shader derlemesi yok.

API'nin şekli

API'nin anlık modlu kısımları için Raylib'den yoğun şekilde ödünç aldık. Bir Raylib oyunu yazdıysan, kas hafızası aktarılır:

import { initWindow, windowShouldClose, beginDrawing,
         endDrawing, clearBackground, drawText, Colors } from "bloom";

initWindow(800, 450, "Hello Bloom");

while (!windowShouldClose()) {
  beginDrawing();
  clearBackground(Colors.RAYWHITE);
  drawText("Hello, Bloom!", 190, 200, 20, Colors.DARKGRAY);
  endDrawing();
}

3B ve fizik işleri için aynı felsefe geçerli: düz arayüzler, saf fonksiyonlar, gizli durum makineleri yok. Bir kamera bir nesne literali. Bir katı cisim bir tutamak. Render hattı, görünmez bir orkestratöre dinleyiciler kaydederek değil, fonksiyonları belirli bir sırayla çağırarak yapılandırılır.

Sıradaki ne

Önümüzdeki birkaç hafta için kısa listemiz:

  • Android native-activity yapıştırıcısını bitir ve Android'i yayınlanabilir ilan et.
  • Sahne grafiği işini “temel”in ötesine, gerçekten kullanışlı olana itele: sorgular, frustum culling iyileştirmesi, instancing yardımcıları.
  • Editör entegrasyonunun ilk halini sahne üzerinde oyununu yeniden başlatmadan iterasyon yapabilesin diye indir.
  • Pong'dan biraz daha büyük bir demo dahil, örnek oyunları açık kaynak yap.
  • Perry derleme hattını ayrıntılı olarak yaz. Birkaç kişi sordu.

Dene

Bloom MIT lisansı altında açık kaynak. Tüm depo GitHub'da. Çatallamadan takip etmek istersen, dokümanlarda 12 satırlık bir hızlı başlangıç var ve vitrin sayfası bizim onunla neler ürettiğimizi listeler.

Her durumda: göz attığın için teşekkürler. Daha büyük parçalar geldikçe burada paylaşmaya devam edeceğiz.