2 puan yazan GN⁺ 2024-05-03 | 1 yorum | WhatsApp'ta paylaş

C ile bir haftada 3D modelleyici yapmak

  • Geçen sonbaharda bir haftalık programlama olayı olan Wheel Reinvention Jam'e katıldım.
  • Amacı, mevcut yazılım sistemlerini farklı bir bakış açısından yeniden gözden geçirmekti.
  • ShapeUp adında bir 3D modelleyici yaptım; bu yazıyı okumadan önce ShapeUp'ın demo videosunu izlerseniz konu daha iyi anlaşılır.
  • ShapeUp doğrudan tarayıcıda kullanılabilir.

Dil seçimi: C

  • TypeScript derleyicisinin yavaşlığına yönelik rahatsızlığımdan dolayı Jam'e katıldım.
  • Esbuild veya Bun'un TypeScript ayrıştırıcısıyla başlamak, TypeScript'in hızlı bir alt kümesini uygulayan bir projeye zemin hazırlar gibi görünüyordu.
  • Ama terminal komutlarının çalıştırılma hızını karşılaştırmaktan ilginç bir demo çıkmayacağını düşündüğüm için yönümü 3D projeye çevirdim.
  • Ray marching signed distance fields (SDF) tekniği sayesinde bir haftada sıfırdan bir 3D proje yapmak mümkün göründü.
  • Aynı karmaşıklıkta bir üçgen tabanlı renderdan çok daha hızlı bir şekilde SDF ile sahne oluşturulabiliyordu.
  • Daha önce SDF shader'ı yazmıştım ama sadece temel seviyedeydi; kodu düzenleyerek modelleme yapmak doğal hissettirmiyordu.
  • Şekli fareyle düzenlemek istemiştim ve bu Jam'in bunu hayata geçirme fırsatı olacağını düşündüm.
  • Projeye ShapeUp adını verdim.

C kullanmanın avantajları

  • C'nin çok basit ve ham bir dil olması, gömülü veri yapılarındaki eksiklikleri ve pointer hatalarını düzeltmek için çok zaman harcayacağım hissi veriyordu.
  • Ancak C'nin sadeliği avantaj oldu:
    • hızlı derlenir
    • karmaşık hesaplamaları saklamaz
    • basit olduğu için sürekli sözdizimi aramak zorunda kalmazsın
    • native ve WebAssembly için kolayca derlenir
  • C'deki eksiklikler, 22 yıl boyunca geliştirdiğim alışkanlıklarla atlatılabilir.
  • ShapeUp tek bir küçük C dosyasından oluşuyor ve çok basit.

ShapeUp'ın veri yapısı

  • Model, Shapes adlı bir yapı dizisiyle temsil ediliyor.
  • Shapes, statik tahsisli bir dizi içinde saklanıyor.
    • Tahsis hatası veya bellek sızıntısı riski yok.
    • 100 Shape sınırı pratikte kısıtlayıcı olmadı.
    • Renderer optimizasyonu için zamanım olmadığı için 100 Shape'e ulaşmadan önce kare hızı düşmüş olacaktı.
    • Vakit olsaydı modeli küçük bloklara bölüp her blok içinde ray marching yapardım.
  • Dinamik bellek yalnızca üç yerde malloc ile ayrıldı.
    • Kaydetme (tüm belgeyi tutabilecek kadar büyük bir arabellek ayırma)
    • OBJ dışa aktarma (tüm köşeleri tutabilecek kadar büyük bir arabellek ayırma)
    • GLSL shader oluşturma (shader kaynağı için arabellek)
  • Tüm durumlarda fonksiyonun sonunda tek bir free var.
  • C'de bellek yönetiminin ne kadar basit olabileceğini gösteren bir örnek.
  • C#, JavaScript, Python gibi dillerde her Shape için ayrı ayrı malloc yapılır ve bu işaretçiler dinamik bir dizi içinde saklanmak üzere zorunlu olur.
  • C ile belleğin düzenini kontrol edebilmek çok iyi.

Kullanıcı arayüzü

  • immediate mode user interface (IMGUI) ile uygulandı.
  • IMGUI tarzı arayüzü seviyorum:
    • hata ayıklaması çok kolay
    • elemanları yerleştirmek için gerçek bir programlama dili kullanmak (CSS, constraints ve SwiftUI'nin aksine)
  • Çoğu IMGUI'de olduğu gibi enum kullanılarak hangi öğenin odakta olduğu veya farenin ne yaptığı izleniyor.
  • Bu projede dinamik diziye veya hash mapa ihtiyaç duymadım; ama gerekseydi, stb_ds.h gibi bir şey kullanırdım.

Raylib kütüphanesinin sorunları

  • C kullanmaya karar vermek iyi oldu, ama raylib sıkıntı çıkardı.
  • Geliştirici deneyimini bozan garip tasarım kararları var:
    • enum beklenen yerde int kullanılmasıyla derleyici tür kontrolü atlanıyor ve fonksiyonlar kendiliğinden açıklayıcı olmuyor
    • temel parametre doğrulaması yapılmaması (tasarım kararı)
    • bağımlılıklara sahip çıkmaması (örneğin GLFW ile ilgili sorunları çözmemeleri ya da yama göndermemeleri)
  • raygui UI kütüphanesi bir oyuncaktan öteye geçmiyor:
    • kayan noktalı sayılar gösterilemiyor
    • üst üste binen veya kesilen öğeler için fare olayı yönlendirmesini yönetmiyor
    • yuvarlatılmış köşe oluşturamıyor
    • iyi bir stil uygulanmıyor
  • Hatalar da var:
    • font değiştirmeyi engelleyen bir bug
    • çizim fonksiyonu üçgenler arasında köşeleri paylaşmadığı için piksel aralıkları oluşuyor
  • Sorun buldukça bildirdim ama çoğu "won't fix" ile kapatıldı; bug raporu yazımı da çok zaman aldığı için bıraktım.
  • OpenGL penceresi sağlıyor oluşu iyi bir şeydi ama o kolaylığın bedeli ağırdı.
  • Neyse ki OpenGL fonksiyonlarını doğrudan kullanabileceğim veya bir işlevi sıfırdan geliştirebileceğim bir çıkış yolu bulabildim.
  • İleride sokol kullanacağım.

Bir haftalık geliştirme süreci

  • ShapeUp, 6 günde tamamlanması gereken dört ana parçadan oluşuyordu:
    1. Kullanıcı arayüzü (3D araçları, klavye kısayolları, yan çubuk, oyun kontrolcüleri)
    2. GLSL shader üreticisi + Ray marching renderer
    3. GPU tabanlı fare seçimi
    4. Dışa aktarma için Marching Cubes
  • Hepsi zor değildi ama doğru önceliği koyup odakta kalmak zordu.
  • Zor ve zaman alan problemleri tasarım yoluyla çözmek veya %90 ihtimalde çalışan basit bir çare uygulamak yardımcı oldu.
  • Bazen bir özelliği bir gün ertelemek, sorunu bilinçaltı bir şekilde bulmama yardımcı oldu.
  • Her zaman çalışan bir 3D modelleyicime sahip olmalıydım; zaman oldukça kalarak kademeli geliştirmeyi hedefledim.
  • Bir piramit inşa etmek gibi düşündüm; katmanlı ilerlersen en tepeye kadar tamamen bitmeyebilir ama nerede bırakırsan bırak, yine de tam bir piramit gibi olmalı.

Proje sonuçları

  • Bir hafta sonra anlamlı bir 3D model yapıp onu .obj olarak dışa aktarabilen bir 3D programım oldu.
  • Çok platformlu çalışıyor ve dosya açma/kaydetme özellikleri var.
  • Proje 2024 satır C kodu ve 250 satır GLSL'den oluştu.
  • Yaklaşık 2300 satırla, işe yarar bir 3D modelleyiciyi gösterilebilmesi biraz şaşırtıcıydı.
  • Jam özeti için ve Handmade Seattle konferansında ShapeUp demosunu göstermem istendi.
  • İnsanlar ShapeUp'a etkilendiklerini belli ettiler ama büyük bir başarı gibi durmuyordu; bu göreli basit bir projeydi.
  • Benim yaptıklarım arasında fark yaratmak istediysem, ne yapacağımı seçebilmek, bunu yapacak bilgiye sahip olmak ve bir hafta içinde bunu tamamlamak için disiplindi.

GN⁺'in görüşü

  • C'nin sadeliği ve hızını vurgulayan ilginç bir projeydi. Ancak C'nin düşük soyutlama seviyesi nedeniyle doğrudan ticari bir projede kullanılması zor görünüyor. Modern 3D modelleme araçlarının tüm özelliklerini C ile sıfırdan uygulamanın ciddi efor gerektireceği düşünülebilir.
  • Bir haftada çalışan bir programı bitirmen etkileyici. Ama uzun vadede bakımı ve özellik genişletmesi düşünülürse C++ ya da Rust gibi dillerin daha iyi bir seçenek olması mümkün.
  • SDF ile render tekniği hızlı ve basit olsa da modelleme esnekliği ve kalite açısından sınırlı kalabiliyor. Ticari modelleme araçları genellikle SubD ya da NURBS gibi yüzey modelleme teknikleri kullanır. Ancak oyunlar veya demo gibi gerçek zamanlılığın kritik olduğu alanlarda SDF render hâlâ güçlü bir kullanım değeri sunuyor.
  • Açık kaynak kütüphane seçiminin zorluğunu iyi gösteren bir örnek. Dokümantasyon, kod kalitesi, destek durumu gibi konuların iyi anlaşılması ve dikkatli seçim yapılması gerekiyor. Kendi implementasyonunu geliştirmek de iyi bir alternatif olabilir.
  • Çalışan bir programı önce yapmak ve sonra kademeli olarak geliştirmek, pratikte de çok işe yarıyor. Temel özellikleri önce tamamlayıp detayları sonraki adımlarda iyileştirmek açısından önceliklendirmeyi iyi ayarlamak önemli görünüyor.

1 yorum

 
GN⁺ 2024-05-03
Hacker News Yorumu
  • Raylib'in kısıtlamaları konusunda yazara tamamen katılıyorum
    • Şu an Raylib ile bir kule savunma tarzı oyun geliştirme sürecindeyim ve benzer kısıtlarla karşılaşıyorum
    • Platformlar arası tam ekran geçiş uyumsuzluğu, ekran modlarını listeleyememe, çalışma zamanında render özelliklerini değiştirememe ve derlenmiş shaderları kaydedememe gibi sorunlar var
    • Raylib prototipleme için iyi, ancak ciddi kısıtlamaları kabullenmeden bunun ötesine geçmek zor
    • Geliştirme o kadar ilerledi ki, şimdi Raylib'i SDL gibi bir seçenekle değiştirmek için çok geç
  • Şekilleri statik bir diziye yerleştirmek, tahsis başarısızlığı ve bellek sızıntısı riskini ortadan kaldıran sevimli bir yaklaşım. Gerçekten de 100 şekil sınırı bir kısıtlama olmuyor.
  • Bu projenin gelişmeye devam etmesini diliyorum. Birkaç ay içinde, Blender/FreeCAD'in öğrenme eğrisinin çok daha yumuşak olduğu belirli kullanım senaryoları için ciddi bir alternatif olabilir.
  • Videodaki canlı demo gerçekten hoşuma gitti. Uygulama geliştirme işini bir yana bırakıp da böyle bir videoyu bir haftada yapmak mümkün olmazdı.
  • Bellek yönetimi gibi farklı kararların tartışıldığı ilginç bir yazı. Crafting Interpreters 2'ye dalıp C'yi yeniden öğrenmeye başlayan biri olarak, C'nin iyi yaptığı şeyleri hatırlattı.
  • Bunu yalnızca 2024 satır C koduyla mümkün kılan çabaya teşekkür ederim :)
  • Tanıdığınız bir aracı kullanarak sadece iyi bir şey üretebilmede gerçekten güçlü bir şey var. Yazı çok iyi yazılmış.
  • C hakkındaki iddialara gerçekten katılıyorum. Özellikle "sözdizimi karmaşık hesaplamaları saklamaz, basit olduğu için sürekli araştırma yapmanız gerekmez" kısmına daha da çok katılıyorum. Ayrıca C ile ilgili bir şey bulmanız gerektiğinde çok kolay ve öğretici oluyor. Bu, sade ve eski bir dilin avantajıdır.
  • Bazen C'nin bize gereken her şey olduğunu düşünüyorum.
  • Etkileyici bir geliştirme hızı. Açıklama videosunu da gerçekten çok eğlenceli buldum!