2 puan yazan GN⁺ 2024-05-03 | 1 yorum | WhatsApp'ta paylaş
  • Wheel Reinvention Jam’in “mevcut yazılımlara yeni bir gözle yeniden bakma” göreviyle ShapeUp ortaya çıktı ve tarayıcıda çalışan demo ile .obj dışa aktarma destekli bir 3D modelleyiciye dönüştü
  • Bir hafta içinde 3D araç yapılabilmesinin anahtarı ray marched SDF oldu; üçgen tabanlı bir rendererdan daha hızlı şekilde renk, yumuşak gölge ve ambient occlusion içeren sahneler uygulanabildi
  • Uygulama tek C dosyası merkezli olacak şekilde basit tutuldu ve model en fazla 100 Shape’i statik dizide tutarak bellek yönetimi yükünü azalttı
  • raylib, OpenGL penceresini hızlıca açmada yardımcı oldu ancak int ağırlıklı API’si, parametre doğrulaması eksikliği, GLFW bağımlılığı ve raygui sınırlamaları nedeniyle bazı yerlerde doğrudan OpenGL kullanmak ya da özellikleri baştan yazmak gerekti
  • Nihai çıktı 2024 satır C ve 250 satır GLSL, toplamda yaklaşık 2300 satırlık bir projeydi; dosya açma-kaydetme, çoklu platform çalıştırma ve .obj dışa aktarmayı destekliyordu

ShapeUp’ın 3D modelleyiciye dönüşme süreci

  • Wheel Reinvention Jam, mevcut yazılım sistemlerine yeni bir bakışla tekrar yaklaşmayı amaçlayan bir haftalık bir programlama etkinliğiydi
  • İlk hedef, yavaş TypeScript derleyicilerinden duyulan rahatsızlıktan yola çıkarak tsc’den daha hızlı çalışan bir TypeScript alt kümesi yapmaktı
    • esbuild ya da Bunun TypeScript parser’ı başlangıç noktası olarak kullanılabilir görünüyordu
    • Ancak başarılı demo en fazla “bir terminal komutu diğerinden daha hızlı bitiyor” seviyesinde kalıyordu ve görsel olarak çekici değildi; bu yüzden yön 3D’ye çevrildi
  • ShapeUp, şekilleri fareyle düzenlemeye odaklanan bir 3D modelleyici olarak geliştirildi
    • Daha önce SDF shader yazma deneyimi vardı ama modeli doğrudan kod değiştirerek oluşturma yaklaşımı doğal gelmiyordu
    • Amaç, SDF tabanlı şekil düzenlemeyi fareyle yapılabilir hale getirmekti

SDF’nin bir haftalık projeyi mümkün kılma nedeni

  • ShapeUp’ın render altyapısı ray marched signed distance fields (SDFs) üzerine kurulu
  • SDF sahneleri, renk, yumuşak gölge ve ambient occlusion içerse bile üçgen tabanlı bir rendererdan daha hızlı uygulanabildi
  • Inigo Quilez’in SDF ile oturup tek seferde Pixar tarzı bir karakter oluşturduğu örnek, teknik yön için önemli bir referans oldu
  • ShapeUp, kod düzenlemek yerine şekilleri doğrudan manipüle ederek SDF modellemeyi ele alıyor

C uygulaması ve veri yapısı

  • ShapeUp C ile yazıldı ve OpenGL penceresi oluşturmak için raylib kullanıldı
  • C’nin seçilme nedenleri hızlı derleme, karmaşık davranışları gizlemeyen sözdizimi, aşinalık ve hem native hem de WebAssembly’ye derlenebilmesiydi
  • Model, Shape struct’larının bir koleksiyonundan oluşuyor
    • Her Shape; konum, boyut, açı, köşe yarıçapı, blob seviyesi, renk, aynalama ekseni ve subtract bilgisi taşıyor
  • Shape listesi dinamik tahsis yerine statik diziyle yönetildi
    • MAX_SHAPE_COUNT değeri 100
    • Durum, Shape shapes[MAX_SHAPE_COUNT], shape_count ve selected_shape ile tutuluyor
    • Bu yaklaşım, tahsis başarısızlığı ve sızıntı ihtimalini ortadan kaldırıyor
  • 100 Shape sınırı pratik kullanımda büyük bir sorun olmadı
    • Renderer optimizasyonuna zaman kalmadığı için 100 Shape’e ulaşmadan önce kare hızı düşüyordu
    • Zaman olsaydı model küçük tuğla benzeri bölümlere ayrılacak ve ray marching her bölüm içinde yapılacaktı

Bellek kullanım yöntemi

  • ShapeUp dinamik bellek tahsisini yalnızca 3 yerde kullanıyor
    • Kaydetme: tüm belgeyi tutacak bir buffer tahsisi
    • .OBJ dışa aktarma: tüm vertex’leri tutacak bir buffer tahsisi
    • GLSL shader üretimi: shader source için buffer tahsisi
  • Her durumda fonksiyonun sonunda yalnızca bir kez free çağrılıyor
  • Her Shape’i ayrı ayrı malloc ile ayırıp pointer’ları dinamik bir dizide tutmak da mümkündü ama bu proje için böyle bir yapıya gerek yoktu
  • C, bellek yerleşimini doğrudan kontrol edebilme açısından avantaj sağladı
  • Dinamik dizi ya da hash map gerekseydi stb_ds.h gibi araçlar kullanılabilirdi

UI’nin uygulanma biçimi

  • UI, immediate mode user interface (IMGUI) yaklaşımıyla geliştirildi
  • IMGUI; debug etmenin kolay olması ve CSS, constraints ya da SwiftUI yerine öğe konumlarını gerçek programlama diliyle belirlemeye izin vermesi açısından avantajlıydı
  • Odaktaki öğe ve fare davranışları Control enum’u ile takip edildi
    • Konum, boyut, açı, renk, taşıma, döndürme, ölçekleme, kamera döndürme ve blob seviyesi gibi etkileşim durumları enum değerleriyle ifade edildi
    • focused_control ve mouse_action, mevcut UI durumunu tutuyor

raylib ve raygui ile tıkanılan noktalar

  • raylib, OpenGL penceresini hızlı açmakta yararlıydı ama zaman geçtikçe geliştirme hızını düşüren bir unsura dönüştü
  • raylib API’sinde özellikle rahatsız edici olan nokta tip bilgisinin yetersizliğiydi
    • enum beklenen yerlerde bile int kullanıldığı için derleyicinin tip kontrolünden yararlanılamıyordu
    • Yalnızca fonksiyon imzasından parametrelerin anlamı net anlaşılmıyordu
    • Örneğin IsGestureDetected(unsigned int gesture) içindeki gesture, kayıtlı bir gesture ID’si gibi görünse de gerçekte Gesture enum’uydu
    • Dokümantasyon header dosyaları merkezli olduğundan hangi int değerinin aslında enum olduğunu anlamak için implementasyona bakmak gerekiyordu
  • Temel parametre doğrulamasının yapılmaması da sorunu büyüttü
    • LoadFileData(const char *fileName, int * dataSize) içinde dataSize NULL olursa segfault oluşuyor
    • Header, dataSize parametresinin output parametresi olduğunu ya da null olamayacağını belirtmiyor
    • Doğrulama eksikliği, basit sorunların izini sürmeyi zorlaştırdı ve bazı durumlarda sessizce hatalı davranışlara yol açabildi
  • Bağımlılık yönetiminde de beklentiyle gerçeklik farklıydı
    • GLFW tarafındaki sorunlarda raylib’nin workaround üretmemesi ya da patch göndermemesi problem oldu
    • Son kullanıcı için pencerenin içeride nasıl oluşturulduğundan çok raylib özelliğinin düzgün çalışıp çalışmadığı önemliydi
  • raygui UI kütüphanesi projede kullanılmak için fazla kısıtlı kaldı
    • Kayan noktalı sayı gösteremediği için float metin alanlarını elle yapmak gerekti
    • Üst üste binen ya da kırpılmış öğelerde fare olayı yönlendirmesini işleyemiyordu
    • UI’de yaygın olan yuvarlatılmış köşeleri desteklemiyordu
    • Güzel görünecek şekilde stillendirmek zordu
  • Hatalar da geliştirme akışını bozdu
    • raygui araç hatası yüzünden aşırı stillendirilmiş varsayılan font değiştirilemedi
    • DrawCircle(...) gibi çizim fonksiyonları üçgenler arasında vertex paylaşmadığı için mevcut matriste ölçekleme ya da döndürme varsa kayan nokta hataları nedeniyle pikseller arasında boşluklar oluşuyordu
  • Bulunan sorunlar bir süre raporlandı ama çoğu “wont fix” olarak kapatıldı; sonrasında raporlamaya son verildi
  • Çözüm olarak ya doğrudan OpenGL fonksiyonları kullanıldı ya da gereken özellikler sıfırdan yazıldı
  • İleride raylib yerine sokol kullanılması planlanıyor

6 günde tamamlanması gereken dört şey

  • ShapeUp’ın 6 gün içinde büyük ölçüde dört bölümü tamamlaması gerekiyordu
    • Kullanıcı arayüzü: 3D gizmo’lar, klavye kısayolları, kenar çubuğu, oyun kumandası desteği
    • GLSL shader üreticisi ve ray marching renderer
    • GPU tabanlı fareyle seçim
    • Dışa aktarma için marching cubes
  • Zorluk, her bir özelliğin kendisinden çok öncelikleri korumakta yatıyordu
  • Zor ya da çok zaman alan problemler ya tasarım değiştirilerek aşıldı ya da vakaların %90’ında çalışan basit çözümlerle geçildi
  • Bazı özelliklerde çözüm, bir gün bekletildikten sonra akla geldi
  • Çalışma yöntemi, her zaman çalışan bir 3D modelleyiciyi koruyup zamanı oldukça aşamalı iyileştirmeler yapmaktı
    • Bu yaklaşım, sadece en sonunda piramit haline gelen bir yapı kurmak yerine, hangi aşamada durulursa durulsun tam bir küçük piramit elde etmeye benzetiliyor

Nihai sonuç

  • Haftanın sonunda ShapeUp, anlamlı 3D modeller oluşturabiliyor ve bunları .obj dosyası olarak dışa aktarabiliyordu
  • Birden fazla platformda çalışıyor, dosya açma ve kaydetmeyi de destekliyordu
  • Kod boyutu 2024 satır C ve 250 satır GLSL idi
  • Yaklaşık 2300 satırla belli ölçüde kullanışlı bir 3D modelleyici yapılabilmiş olması dikkat çekici bir sonuçtu
  • Projenin kendisi görece basit olsa da ne yapılacağını seçme sezgisi, bunu yapabilecek bilgi birikimi ve bir hafta içinde bitirme disiplini belirleyici oldu

1 yorum

 
GN⁺ 2024-05-03
Hacker News yorumları
  • Raylib’in sınırlamaları konusunda yazarla tamamen aynı fikirdeyim. Şu anda Raylib ile başlamış bir tower defense tarzı oyun yapıyorum ve aynı sınırlamalarla birlikte başka pek çok sorun yaşıyorum.
    Örneğin platformlar arasında tam ekran geçişi tutarlı çalışmıyor, ekran modları listelenemiyor, çalışma sırasında render özelliklerini açıp kapatmak zor oluyor ya da derlenmiş shader’ları kaydetme gibi sorunlar var.
    Yine de Ray’in bu kütüphaneye verdiği emek için minnettarım ve desteklemeye devam etmeyi düşünüyorum. Raylib hızlı prototip üretmek için harika, ama ciddi kısıtları kabul etmeden onun ötesine geçmek kolay değil.
    Kesinlikle çok şey öğrendim, ama bu saatten sonra Raylib ile ilgili tüm kodu SDL gibi bir şeyle baştan değiştirmek için geliştirme fazla ilerledi

    • Raylib adının ray tracing’den değil, yapan kişinin adı olan Ray’den gelmesi eğlenceli bir detay.
      Things Unexpectedly Named After People: https://notes.rolandcrosby.com/posts/unexpectedly-eponymous/
    • Raylib’e başlamak kolay ama proje biraz karmaşıklaşınca tersine ısırmaya başlıyor. Buna karşılık SDL ilk kurulumda daha fazla zaman istiyor, ama proje büyüdükçe çok iyi ölçekleniyor. Kod kalitesi de istisnai derecede iyi
    • Raylib’de muhtemelen ileride de düzeltilmeyecek pek çok sorun var, ama tam ekran sorununu sadece Raylib’in suçu olarak görmek zor. Windows’ta tam ekran onlarca yıldır fiilen kullanılamayacak kadar bozuktu, diğer platformlarda da durumun benzer olması muhtemel.
      Bugünlerde strateji sadece kenarlıksız pencere modunu kullanmak ve gerçek tam ekran yokmuş gibi davranmak
    • Benzer hissediyorum. Yaklaşık 2 ay önce projeye başlarken Raylib’i seçtim ve temel şeyler gerçekten çok kolay ilerliyor, ama kullandıkça küçük ve rastgele can sıkıcılıklar sürekli ortaya çıkıyor. Artık Raylib kullanımını geri almak için projeye fazla yatırım yaptım.
      Şu anda en büyük sorun font işleme ve metin render etme. TTF fontlar yerine önceden hazırlanmış bitmap fontlara geçmem gerekecek gibi görünüyor, ama bu da ileride yerelleştirme yaparken epey acı verici olabilir.
      Love2D’den geçtikten sonra en çok eksikliğini hissettiğim iki özellik, çok renkli metni kolayca render etmek ve texture’ları kolayca kesip tekrar ettirmek ya da tile etmek. Raylib’de renk işaretleme temelli olarak metni bizzat parçalara ayırıp, genişlik ofsetleri uygulayıp, satır sonlarını da hesaba katarak her parça için çizim fonksiyonunu çağırmak gerekiyor.
      Ekrana çok fazla metin çizince FPS’in de ciddi biçimde düştüğünü düşünüyorum; belki de metin çizim çağrılarının batching’i bozuluyordur. Eskiden tile texture çizme fonksiyonu vardı ama nedense kaldırıldı
    • Bunu görünce raylib’e bakmak istedim. WebAssembly ile çalışan sevimli örnekler var: https://www.raylib.com/examples.html
      Wasm ve tarayıcı 3D/2D grafiklerinde beni hep rahatsız eden şey, scroll gibi küçük sorunların sık sık görünmesi. Buradaki “Background scrolling & parallax” örneğine bakın: https://www.raylib.com/examples.html
      Birden fazla cihazda test ettim ve gözlerim bozuk değilse bunun kesinlikle akıcı kaydırma olmadığını söyleyebilirim. 2024 yılında 2D akıcı kaydırmanın hâlâ çözülmemiş bir sorun olması akıl alır gibi değil
  • “Shape’leri statik olarak ayrılmış bir dizide tutuyorum. Ne allocation failure var ne memory leak ne de gereksiz karmaşa. Bayıldım. 100 shape sınırı pratikte bir kısıt değildi. Renderer’ı optimize etmeye neredeyse hiç zaman ayırmadığım için 100’e ulaşmadan önce zaten frame rate düşecekti.”
    Son zamanlarda gördüğüm erken optimizasyondan kaçınma konusundaki en iyi örnek

    • Bence daha çok bunun tersi. Erken soyutlama ve genellemeden kaçınmak
    • Bir şeyi gerçekten yapan insanlarla, nasıl yapılması gerektiği üzerine oturup tartışan insanlar arasındaki farkı gösteren en iyi örnek
  • Gerçekten ilgi çekici bir yazı; bellek yönetimi biçimi ve raylib’de karşılaştığı sorunlar gibi çeşitli kararları anlatması hoşuma gitti. Tam da Crafting Interpreters’ın 2. bölümüne girip C’yi yeniden gözden geçiriyorken, C’nin iyi olduğu şeyleri yeniden hatırlatması güzel oldu

  • Videodaki canlı demo gerçekten çok iyi. Uygulamayı yapmak bir yana, ben deneseydim o videoyu bile bir hafta içinde hazırlayamazdım gibi geliyor

    • Videoyu yapmak uygulamadan daha uzun sürdü. YouTuber’ların bunu nasıl bu kadar sürekli yaptığını bilmiyorum
  • Uzun zaman önce masaüstü telefonlar için bir işletim sistemi üzerinde çalışmıştım. Yalnızca 64K RAM vardı, bu yüzden hiç dinamik bellek yönetimi yoktu; derleyicinin her şeyi derleme zamanında yerleştirmesi için bolca statik değişken kullanıyorduk.
    Pek çok uygulamanın aslında hiç dinamik bellek yönetimine ihtiyaç duymayabileceğini unutmak kolay. Birkaç sabit boyutlu buffer ayırıp, o buffer dolduğunda oluşacak istisnai durumu temizce ele almak çoğu durumda yeterli olabilir.
    Bu bağlamda C aslında çok daha güvenli. Memory leak yok; dert edilmesi gereken tek şey buffer overflow. Tüm değişkenler statik olarak ayrılmışsa bunu sizeof kullanımına dikkat ederek yönetmek mümkün.
    Bu, günümüzde Rust ve Go’nun harika seçenekler olmadığı anlamına gelmiyor; ama mütevazı eski C hâlâ gayet işe yarıyor ve kâbus gibi karmaşık olmak zorunda değil

  • Konudan biraz sapıyor ama, metni bulanık görünmeyen ilk WebAssembly arayüzünü gördüğüme sevindim. Gerçekten ilk kez görüyorum.
    Bunu programlar ve bazı işletim sistemleri, örneğin Windows ölçeğine kadar genişletirsek, son birkaç yılda metin rasterizasyonu yönteminin ortak moda ve varsayılan ayar hâline gelmesiyle genel bir sorun oluştu.
    Ne yazık ki kullanıcı çoğu zaman net metin elde etmek için anti-aliasing’i kapatamıyor; nadiren böyle bir seçenek olsa bile menüler gibi arayüzlerde anti-aliasing yine uygulanıyor

    • O net metin o kadar da etkileyici değil. Yazı tipinde eğriler yok ve smoothing de yok. Hangi çözünürlükte olursa olsun keskin ve blok gibi köşeli metin gibi görünecek
    • WebAssembly ile rasterizasyon arasında nasıl bir bağlantı olduğunu merak ediyorum. Gerçekten ilginç görünüyor
  • Bu tür projeleri gerçekten seviyorum. C’nin düşük seviye doğası hâlâ hoşuma gidiyor. Şu sıralar daha çok Rust ve Elixir/Erlang kullanıyorum ama C’nin sadeliğini ve açıklığını sık sık özlüyorum.
    Bu yüzden Zig de epey kullanıyorum; C’nin felsefesini büyük ölçüde korurken onu oldukça iyi geliştiren bir dil

    • Erlang da oldukça basit bir dil
  • C hakkındaki değerlendirmesine gerçekten katılıyorum. Özellikle “karmaşık davranışları sözdiziminin arkasına gizlemiyor. Sürekli dönüp bakmayı gerektirmeyecek kadar basit” kısmı çok doğru ve ayrıca C hakkında bir şeyi araştırmanız gerektiğinde de bu genelde çok kolay ve faydalı oluyor
    Basit ve eski bir dil olmanın kendine özgü avantajları var

  • Her Shape için ayrı ayrı malloc çağırıp bu işaretçileri dinamik bir dizide tutarsanız, işi kesinlikle kendi kendinize daha zor hale getirebilirsiniz. C# gibi bir dilin bu tür bir tahsis yapısını zorunlu kıldığı yönünde bir ifade var ama yazarın C'de yaptığı gibi sabit bir struct dizisi kullanmayı C#'ta neyin engellediğini merak ediyorum

    • Hiçbir şey engellemiyor. C#'ta da bu şekilde struct dizileri kullanmak pek de nadir değildir
  • Keşke biri bu projeyi sürdürse. Birkaç ay daha cilalanırsa belirli kullanım alanlarında Blender veya FreeCAD'e ciddi bir alternatif olabilir ve öğrenme eğrisi de çok daha yumuşak görünüyor

    • Daha gelişmiş ama hâlâ ücretsiz olan MagicaCSG'ye de bakmak iyi olabilir: https://ephtracy.github.io/index.html?page=magicacsg#ss-caro...
    • Düzeltme: Aha, bu program zaten marching cubes ile mesh dışa aktarmayı destekliyormuş. Sitedeki YouTube videosuna bakabilirsiniz. Bunu bilmiyordum
      Ancak temelde SDF ile çalıştığı için, üçgenler ve köşeler kullanan geleneksel mesh'lerden hem modelleme deneyimi hem de saklanan veri açısından farklı
      SDF'yi mesh'e dönüştürmek marching cubes gibi yöntemlerle mümkün ama ortaya çıkan veriyi sonrasında Blender benzeri uygulamalarda büyük olasılıkla yine temizlemek gerekir
      Eğer render motoru da SDF tabanlıysa, SDF harikadır. Ama çoğu öyle değil
      Bunu zaten biliyorsanız kusura bakmayın
    • SDF seviyorsanız, başlamak için Womp oldukça iyi. Tinkercad de başlangıç seviyesi bir CAD aracı olarak gayet iyi
    • Dune3D ve Salome-Platform'a da bakmak iyi olabilir
    • Blender'a alternatif olabilir ama ne yazık ki CAD işi için bir alternatif olacak gibi görünmüyor