- Yalnızca CSS ile 3D DOOM render eden bir deney; tüm duvarlar ve nesneler
<div> ile ve 3D dönüşümler (transform) kullanılarak oluşturulmuş bir proje
- Oyun mantığını JavaScript üstleniyor, ancak render işlemi tamamen CSS tarafından yapılıyor; böylece tarayıcıların ve modern CSS'in sınırları araştırılıyor
- Trigonometrik fonksiyonlar,
clip-path, @property, SVG filtreleri, anchor positioning gibi modern CSS özellikleri kullanılarak duvarlar, zemin, ışıklandırma, sprite'lar ve patlama efektleri bile uygulanmış
- CSS'te kamera kavramı olmadığından, bakış açısı oyuncu yerine dünyanın hareket ettirilmesi yöntemiyle işleniyor ve tüm hareketler özel özellik güncellemeleriyle kontrol ediliyor
- WebGL düzeyinde performans sunmasa da, CSS'in ifade gücü ve hesaplama yeteneğinin genişletilebileceğini gösteren bir örnek
CSS ile 3D DOOM render etme
- DOOM'u yalnızca CSS ile render eden deneysel bir proje; tüm duvarlar, zeminler ve nesneler
<div> ile oluşturuluyor ve 3D dönüşümler (transform) ile yerleştiriliyor
- Oyun mantığı JavaScript'te çalışıyor, ancak render işlemi tamamen CSS tarafından yapılıyor
- Projenin amacı tarayıcıların ve modern CSS'in sınırlarını keşfetmek
Lise matematiğine geri dönmek
- Orijinal DOOM'un WAD dosyası verileri (vertices, linedefs, sidedefs, sectors) çıkarılarak binlerce
<div> ile statik sahne oluşturuluyor
- Her duvarın başlangıç-bitiş koordinatları ile zemin-tavan yükseklikleri CSS özel özellikleri üzerinden aktarılıyor
hypot() ve atan2() CSS fonksiyonlarıyla duvar uzunluğu ve dönüş açısı hesaplanıyor
- JavaScript ham veriyi iletiyor, CSS ise trigonometrik hesaplamaları yaparak render işlemini gerçekleştiriyor
- Oyun döngüsü ile render edici ayrılmış durumda; JS yalnızca durum yönetimi ve koordinat güncellemesiyle ilgileniyor
Koordinat sistemi dönüşüm sorunu
- DOOM, Y ekseninin kuzeye doğru arttığı bir 2D koordinat sistemi kullanırken, CSS 3D'de Y yukarıyı, Z ise izleyici yönünü gösteriyor
- Dönüşüm sırasında koordinatları eşleştirmek için
translate3d(x,-z,-y) biçimi kullanılıyor
rotateY(atan2(var(--delta-y), var(--delta-x))) hesabının ek dönüşüm olmadan çalışması dikkat çekiyor
Kamera yerine dünyayı hareket ettirmek
- CSS'te kamera kavramı olmadığı için, oyuncu yerine dünyanın ters yönde hareket ettirilmesi yöntemi kullanılıyor
- JS tarafında yalnızca
--player-x/y/z/angle olmak üzere dört özel özellik güncelleniyor
translate: 0 0 var(--perspective) ile bakış düzeltmesi yapılıyor; rotateY ve translate3d ile görüş dönüşü ve konum hareketi uygulanıyor
- Tüm hareketler yalnızca özellik güncellemeleriyle işleniyor
Zemin, yatırılmış bir div
- Varsayılan DOM öğeleri dikey düzlem olduğu için, zemin
rotateX(90deg) ile yatırılarak yatay yerleştiriliyor
clip-path, polygon(), path() ile karmaşık çokgen alanlar ve delikler ifade ediliyor
- Modern CSS'in
shape() fonksiyonu sayesinde yüzde tabanlı yollar ve evenodd kuralı birlikte kullanılabiliyor
Doku hizalama
- Bitişik sektörler arasında doku kopmaması için dünya koordinatı tabanlı
background-position kullanılıyor
- Tüm sektörler aynı doku ızgarasını paylaştığından sınırlar pürüzsüz biçimde birleşiyor
Kapılar, liftler ve @property animasyonu
- Kapı açılması, sektör tavanının yükseltilmesiyle yapılıyor; kapsayıcı
<div> öğesinin transform değeri CSS transition ile işleniyor
- Liftlerde oyuncu da birlikte hareket ettiğinden, JS tarafında
--player-z senkronize ediliyor
@property ile özel özellikler sayısal türde kaydedilerek yumuşak düşme ve hareket efektleri elde ediliyor
Sprite'lar ve aynalama
- Düşman sprite'ları, her zaman kameraya bakan billboard yöntemiyle gösteriliyor
- 8 yönün yalnızca 5 seti gerçek görsel; kalanları yatay yansıtma (
scaleX) ile işleniyor
steps() animasyonu ile yürüme, saldırı ve ölüm kareleri arasında geçiş yapılıyor
- Tüm düşmanların aynı anda yürümesi sorunu, JS tarafındaki rastgele
animation-delay ile çözülüyor
Mermiler, patlamalar ve silah efektleri
- Roketler ve ateş topları gibi nesneler, CSS animasyonlarıyla A→B hareketini otomatik olarak gerçekleştiriyor
- JS yalnızca başlangıç-bitiş koordinatlarını ve süreyi ayarlıyor; çarpışma olduğunda öğe kaldırılıyor ve patlama sprite'ı oluşturuluyor
- Patlama ve mermi dumanı,
steps() tabanlı 3 karelik animasyonun ardından otomatik siliniyor
Işıklandırma ve filtreler
- Sektör bazlı parlaklık değeri
--light özelliğiyle tanımlanıyor, iç öğeler bunu filter: brightness() ile devralıyor
- Yanıp sönen ışıklar için
@keyframes ile --light değeri periyodik olarak değiştiriliyor
- Saydam düşman (Spectre), SVG filtreleri (
feColorMatrix, feTurbulence, feDisplacementMap) ile bozulmuş bir silüet olarak gösteriliyor
Duyarlı arayüz ve anchor positioning
- Oyun mobil uyumlu; HUD,
flex-wrap ile satır kırabiliyor
- Silah sprite'ları, HUD yüksekliğine göre
anchor-name / position-anchor ile otomatik konumlandırılıyor
- Dokunmatik kontrol düğmeleri de aynı anchor yöntemiyle yerleştiriliyor
İzleyici modu
- Haritanın tamamını görme ve üçüncü şahıs takip kamerası destekleniyor
- CSS'in
sin() ve cos() fonksiyonlarıyla oyuncunun arkasındaki kamera konumu hesaplanıyor
rotate ve translate özellikleri ayrıştırılarak yumuşak bakış açısı geçişleri sağlanıyor
- JS yalnızca konum ve açı bilgilerini güncelliyor; kamera matematiğini CSS üstleniyor
Culling ve performans
- Binlerce 3D öğe nedeniyle tarayıcı compositor yükü oluşuyor
- JS tabanlı culling: görüş alanı dışındaki öğeler
hidden yapılıyor
- CSS tabanlı culling deneyi: hesaplanan değerlerle
visibility kontrol ediliyor, type grinding hilesi kullanılıyor
if() fonksiyonu standartlaşırsa bunun yerini daha sade koşul ifadeleri alabilir
Derinlik sıralaması
- Tarayıcı derinlik sıralamasını (
z-order) otomatik olarak yapıyor
- Aynı düzlemdeki nesnelere çok küçük ofsetler verilerek titreme önleniyor
DOOM'un “hileleri” ve gökyüzü işleme
- Orijinal DOOM, gökyüzünü 2D doku olarak “duvarın” üstüne çizen bir projeksiyon hilesi kullanıyor
- CSS render edicisi gökyüzünü gerçek 3D uzaya yerleştirmek zorunda olduğundan, bazı sahnelerde haritanın arkası görünür hale geliyor
- Çözüm olarak culling aşamasında gökyüzü duvarlarının arkasındaki öğeler render dışında bırakılıyor
Sonuç — CSS'in sınırları ve olanakları
- Tüm oyun döngüsü JS ile, render işlemi ise saf CSS tabanlı olarak ayrılmış
- Trigonometrik fonksiyonlar,
@property, clip-path, SVG filtreleri, anchor positioning gibi modern CSS özellikleri sonuna kadar kullanılmış
- WebGL düzeyinde performans vermese de, CSS'in ifade gücünün genişletilebileceğini kanıtlıyor
- Safari ve Chrome'da 3D ile ilgili çok sayıda hata ve performans sorunu bulunmuş
- Nihai sonuç: “CSS ile DOOM çalıştırılabilir mi?”
→ Evet, mümkün. Yes, it can.
1 yorum
Hacker News yorumları
"Bunu DOOM çalıştırır mı?" türü insanlar bence devletin uzay itki sistemleri departmanında işe alınmalı
Onlar parmaklarını oynatmak için fazla sıradışı görevler gerektiren insanlar
Bu, "yapabildiğimiz için yapıyoruz" türü bir proje gibi görünüyor
CSS başlangıçta bildirimsel bir stil diliydi ama artık koşullar, matematik fonksiyonları ve render hileleri eklenince giderek programlanabilir bir sisteme dönüşüyor
Asıl mesele "CSS ile DOOM çalıştırılabilir mi" değil, başlangıçta bunun için tasarlanmamış bir katmana ne kadar mantık ittiğimiz
CSS, programlama dili olma arzusunu saklıyor ama sonunda tamamen yanlış bir soyutlamaya dönüşmüş durumda
Eskiden açılır menüler, tooltip'ler ve yerleşim için JS gerekirdi ama artık anchor konumlandırma ya da koşullar (
if()) bile CSS özellikleriyle belirtilebiliyorAnimasyonlar, detay aç/kapat davranışları ve erişilebilirlikle ilgili efektler bile artık CSS ile yapılabiliyor
CSS ile 3D sahneler oluşturmak zaten uzun zamandır mümkündü ama etkileşim için JS gerekiyordu
Artık x86CSS projesi gibi örneklerle JS olmadan da bir CPU'yu yalnızca CSS ile emüle etmek mümkün
Bu yüzden DOOM'un da saf CSS ile gerçek zamanlı olarak uygulanıp uygulanamayacağını merak ediyorum
Bu örnek, insanların neden TypeScript tabanlı CSS istemeye başladığını gayet iyi gösteriyor
Sadece Chrome'da çalışan
if()gibi özellikler yüzünden geliştiriciler böyle hilelere başvuruyorÖrneğin,
animation-delayve@keyframeskullanarak görünürlük aç/kapa davranışını taklit eden numaralar yapılıyorCSS
if()standartlaşırsa bu tür hack'ler olmadan temiz koşul işlemleri mümkün olacakDOOM hile kodları IDDQD ve IDKFA ne yazık ki çalışmadı
Eskiden bir div'e yuvarlatılmış köşe yapmak için dört GIF gerektiği günleri hatırlatıyor
Gerçekten etkileyici! Tek bir div'i silerek bile duvarların içinden geçme (wall hack) mümkün
.walliçin sadeceopacity: 0.7verirseniz eski usul şeffaf duvar hilesi hissini birebir yakalayabiliyorsunuz"Bunu nerede bizzat deneyebilirim?" diye düşündüm; cssdoom.wtf üzerinden mümkün
Chromium'da ise daha da takılıyordu ve strafing tuşlarını bulamadım
Yine de genel olarak şaşırtıcı bir uygulama
CSS, komite tasarımının sınırlarını gösteren en tipik spesifikasyonlardan biri
SVG ile birlikte "en çirkin görünen spesifikasyon" yarışına giriyor
Bu harika uygulamaya dair bir ek not:
Aslında hareket eden oyuncu değil, dünyanın kendisi
Kamera ise yalnızca görüş açısını (frustum) hesaplamak için kullanılan kavramsal bir araç