Üretilen meshlerdeki delikler nereden geliyor
Cinevva CTO'su ve Kurucu Ortağı Oleg Sidorkin tarafından

TRELLIS.2 ile bir kuş ürettim, rigledim ve animasyon oynatıldığı anda iki kanadın altında da delikler belirdi. Gövdenin içinden karşı tarafı doğrudan görebildiğiniz parlak yarıklar vardı. Önce rig aracı suçlandı; gerçekten de kendine özgü bir sorunu vardı, ancak delikler herhangi bir kemik meshe dokunmadan önce zaten oradaydı. Bu yazı, ardından yürüttüğümüz araştırmayı anlatıyor: deliklerin aslında ne olduğunu, onları düzeltmeyen dokuz yöntemi, bunların neden işe yaramadığını ve işe yarayan tek yöntemi. Çıkmaz yolları bilerek dâhil ediyorum. Bunların çoğu ilk akla gelen yöntemlerdi ve her biri, sonunda bulduğumuz çözümün dayandığı bir şeyi bize öğretti.
Birinci adım: deliğin ne olduğu konusunda anlaşmak
Mesh denetleyicimiz sıfır sınır kenarı bildirdi ve ışın paritesi testi yüzeyin kapalı olduğunu söyledi. Bu yüzden ekibe meshin temiz olduğunu söyledim. Değildi ve yarıkları kırmızıyla işaretleyen kişi itiraz etmekte haklıydı. Bir mesh kapalı olup yine de tüneller içerebilir. Bunları yakalayan tek ölçü, kabuk başına kulp sayısını veren Euler karakteristiğidir: V eksi E artı F. Kuşta bunlardan 69 tane vardı. Dışa aktarımdaki her duvar çift katmanlıydı; dolayısıyla dış duvardaki bir yarığın arkasındaki boşluğa bağlanması bir açıklık değil, kulptur ve açık kenar arayan hiçbir yöntem bunu göremez.
İkinci ve daha utanç verici ders: Delikler karanlık olur diye bir süre karanlık pikselleri inceleyerek delik aradım; sonunda biri önce resme bakmamı söyledi. Delikler parlaktı. Gri bir kıvrımın arkasından görünen, karşı duvardaki yeşil dokunun ince parçalarıydı. Doğru şeye yöneltmediğiniz bir algoritma size hiçbir şey söylemez ve onu yöneltmenin en ucuz yolu bakmaktır.
İkinci adım: onları oluşturan aşamayı bulmak
TRELLIS.2 bir meshi üç adımda oluşturur. Bir kod çözücü, 1024 küp çözünürlükte ham bir yüzey üretir. Dar bantlı bir dual contouring geçişi yüzeyi yeniden meshler. Bir sadeleştirici yüz sayısını yüz bine indirir ve doku bake edilir. Her adımın ardından geometriyi dışa aktarıp her seferinde aynı ışın demetini kanat kökünden geçirdik.
Sorun ham yüzeyde başlıyor. Bu yüzey açık yapraklardan oluşan bir yığın: Çoğu eksik tek bir quaddan oluşan 266.242 sınır döngüsü ve 65.371 manifold dışı kenar. Kanat kökünde test ışınlarının üçte biri doğrudan karşıya geçti. Ardından çalışan delik doldurma işlemi neredeyse hiçbir şeyi kapatmıyor; beş milyon yüzden yalnızca 144'ünü. Yeniden meshleme aracı, zorunlu olarak işaretsiz bir mesafe alanı üzerinde çalışıyor. Kullanabileceği bir iç veya dış bilgisi olmadığı için her yaprağın çevresine ince, ofsetli bir kabuk sarıyor. Bu nedenle her duvar, aralarında yaklaşık dört voksel olacak şekilde çift katmanlı hâle geliyor ve her gözenekli şerit bu şekilde içinde yarık bulunan bir kıvrıma dönüşüyor. Sadeleştirme daha sonra sorunu kötüleştiriyor ve bunu rastgele yapıyor: Aynı ham mesh iki kez sadeleştirildiğinde 69 ve 107 tünel oluştu.

Bu harita ilk gerçek ipucuydu. Gözeneklilik rastgele değil. Parçalar arasındaki her birleşim noktasında bulunuyor, başka hiçbir yerde bulunmuyor.
İşe yaramayan yöntemler
Bunları kabaca ne kadar bariz göründüklerine göre sırayla denedik.
| deneme | sonuç |
|---|---|
| 1024 yerine 1536 çözünürlük | beş test modelinin üçünde daha kötü. Daha yüksek çözünürlük daha fazla ince teması yakalıyor ve bunların her biri yeni bir birleşim oluşturuyor |
| yerleşik temizleme zinciri | manifold dışı kenarları azaltmak yerine artırdı |
| MeshLib ile her küçük deliği doldurmak | 266 bin döngü dolduruldu, kanat kökü değişmedi. Oradaki yüzey hiç üretilmemişti, dolayısıyla doldurulacak bir döngü yoktu |
| winding number ile işaretli yeniden meshleme | meshi binlerce kabuğa ayırıyor. Ham çıktının işaret atanabilecek bir iç bölgesi yok |
| 2 ve 3 voksellik daha geniş contouring bandı | toplamda daha az tünel, ancak yarık daralmak yerine genişliyor. Küçük tüneller birleşip büyük tünellere dönüşüyor |
| voksel yapısına morfolojik kapatma uygulamak | kabuğu katılaştırıyor; kod çözücü daha sonra içerideki her vokselin içinde yüzey üretiyor, yüz sayısı 4 katına çıkıyor, 969 tünel oluşuyor |
| nihai meshi genel olarak yeniden vokselle meshlemek | genus 0 ve yarık kapalı. Ayrıca her tüy kenarı yumuşadı. Görülür görülmez, haklı olarak reddedildi |
| kod çözücünün kesişim eşiğini bias etmek | açık kenarlar 65 binden 58 bine düştü. Gürültü |
| eksik komşu vokselleri tamamlamak | kuşun tamamında 560 örnek. Gürültü |

Genel kapatma üzerinde biraz durmaya değer, çünkü bir modelleme sanatçısının ilk başvuracağı yöntem budur: Voksel yeniden meshlemeyi aralıktan biraz daha büyük bir boyutta çalıştırmak. Topolojik olarak işe yarıyor. Ancak model hakkında da yalan söylüyor. Üretici keskin tüy kenarları oluşturmuştu; bizse kapalı bir kanat kökü uğruna bunlardan vazgeçmiş olacaktık. Dokuz denemeden ikisi tünel sayısını azalttı ve ikisi de herhangi biri yalnızca sayıya değil kuşa baktığı anda reddedildi.
Kodu okumak
Bu noktada yapılabilecek tek dürüst şey, kod çözücüyü bir kutu gibi ele almayı bırakıp çıktısının nasıl meshe dönüştürüldüğünü okumaktı. TRELLIS.2, O-Voxel adlı bir temsil kullanıyor ve çıkarım kodu kısa. Her aktif voksel bir dual vertex ve köşesinden çıkan her grid kenarı için bir tane olmak üzere üç logit barındırıyor; her biri "yüzey bu kenarı kesiyor" anlamına geliyor. Sıfırın üzerindeki bir logit, o kenarın çevresindeki dört vokseli bağlayan bir quad üretiyor. Dört vokselden herhangi biri eksikse quad hiçbir uyarı vermeden atılıyor. Hepsi bu. Modelin hiçbir yerinde işaret alanı, doluluk ya da iç ve dış bilgisi yok.
Kod çözücüyü, eşik uygulanmadan önce aynı kuş üzerindeki bu logitleri dökecek şekilde yamaladık ve saydık.
Eksik komşular: 2,7 milyon vokselin 560'ı. Sıfıra yakın logitler: 8 milyonun 8.600'ü. Sorun bunların hiçbiri değil. Asıl sorun, her grid yüzünün çevresindeki dört kenara baktığınızda ortaya çıkıyor. Kapalı bir yüzey, bir yüzün sınırını çift sayıda keser. 126.542 yüzde, yani yüzlerin %2,3'ünde dört bayrak birbiriyle uyuşmuyor ve her tek sayılı yüz tanım gereği açık ya da manifold dışı bir kenar oluşturuyor. Üstelik güvenle uyuşmuyorlar. Tek kesişimli bir yüzdeki yalnız bayrağın logiti +10 iken komşuları -4'te duruyor. Bir bayrağı tersine çevirip pariteyi geri getirmenin medyan maliyeti 2,3 logit. Bu, eşikteki küçük bir sapma değil. Model, kapanması mümkün olmayan bir yüzeyden emin.

Ardından, sabit kameramızın bütün hafta baktığı yüzey noktasında şeridin kendisini ölçtük. Bu noktanın on iki voksel çevresinde, tek bir düz yaprağın gerektireceğinden 2,1 kat fazla aktif voksel var. Karşılaştırma için göğüste bu oran 1,2. Kanadın altı ile gövde, tek bir voksel katmanının içinde ilerleyen iki ayrı yüzey. Temsil, voksel başına tek bir vertex barındırdığı için ikisini birden tutamıyor. Eğitim sırasında böyle bir vokselin ground truth'u, her iki yüzeyin "herhangi bir kesişim" bayraklarıyla birlikte alınan en küçük kareler ortalaması oluyor; bu da kendi içinde tutarsız. Dolayısıyla model orada bir tarak üretmeyi sadakatle öğrenmiş. Yeniden meshleme aracı tarağı bir plaka içine sarıyor ve plakada tüneller oluşuyor.
Şeridin genişliği geometriden çıkarılabilir: kabaca bir vokselin temas açısının tanjantına bölünmesi. Doksan derecede birleşen iki yüzey, bir voksellik bir kıvrım oluşturuyor ve sorun çıkarmıyor. Gövdeye düz biçimde yaslanan bir kanat, tüy üstüne tüy, zemine basan ayak parmakları; bunların tümü on ya da on beş voksel genişliğinde bir şerit oluşturuyor. Gözeneklilik haritasının açıklaması bu.
Bayrakları en iyi açıklayan köşe başına bir iç/dış işareti çıkarıp ardından bayrakları yeniden türeten prensipli onarımı da denedim. İşe yaraması mümkün değil. İşaret alanı yalnızca kod çözücünün ürettiği bir voksellik kabukta mevcut ve bu kabuğun dışına yapılan her uzatma kontrolden çıkıyor. On iki tur sonunda voksel sayısı işe yaramaz verilerle ikiye katlandı.
Bir modelleme sanatçısının yapacağı şey
Bir modelleme sanatçısı iki yüzeyi asla aralarında kıl payı mesafe kalacak şekilde bırakmaz. Kanadı gövdeyle birleştirir ve arada bir kıvrım oluşmasına izin verir. Genel kapatma bunu her yerde yapıyor; sorun da bu. Öyleyse yalnızca iki yüzeyin bulunduğu yerde yapın.
Dedektörün arkasındaki fikir tek satırda anlatılabilir. Her vokselin dört voksel içindeki aktif komşularını sayın. Tek bir yüzeyde bu sayı her yerde aynıdır ve medyana eşittir. İki yüzey aynı katmanı paylaştığında sayı yaklaşık iki katına çıkar. Medyanın 1,5 katının üzerindeki her şeyi işaretleyin, işaretli kümeyi bir voksel büyütün ve her noktaya birim küp yerleştirin. Yüzeylerin arasındaki boşluk katılaşır, contouring geçişi tek bir duvar görür ve modeldeki başka hiçbir şeye dokunulmaz.

Önce MeshLib'i GPU contouring işleminin yerine kullanarak bir kırpma üzerinde test ettik. Elli üç kabuk ve on dokuz kulp, iki kabuk ve beş kulba dönüştü. Ardından bunu kod çözücü ile dışa aktarım arasındaki gerçek pipeline'a yerleştirip aynı kuşu gerçek yeniden meshleme aracından ve gerçek doku bake işleminden geçirdik.

| tüneller | manifold dışı kenarlar | kanat kökü çıkıntısı | sırttaki delik | |
|---|---|---|---|---|
| olduğu gibi | 79,5 | 318 | var | var |
| dolgu, yarıçap 3 | 84,5 | 97 | yok | yok |
| dolgu, yarıçap 4 | 72 | 38 | yok | yok |
Çıkıntı yok oldu. Kuşun sırtındaki, aynı birleşimin arkadan görülen hâlinde bulunan ve peşinde bile olmadığım ikinci bir delik de yok oldu. Tüyler ayrı kalıyor, ayak parmakları ayrı kalıyor ve küpler, attribute volume'un zaten veri barındırdığı kabuğun içinde bulunduğu için doku normal biçimde bake ediliyor. Dört dakikalık bir işe altı saniye ekliyor. Artık varsayılan olarak açık ve yarıçapı dört.

Bunun düzeltmediği şeyler ve bize anlattıkları
Tünel sayısı neredeyse hiç değişmedi ve nedenini açıkça belirtmek istiyorum. Kalan tüneller, ince bir parçanın sona erdiği ve çift katmanlı duvarın kapanarak sıkıştığı tüy ve ayak parmağı uçlarında. Küçükler, görünmüyorlar ve farklı bir mekanizmadan kaynaklanıyorlar. Dolgu, görünür deliklerin bulunduğu parça temaslarını hedefliyor. Asıl düzeltmenin üretimin önceki aşamalarında yapılması gerekiyor. Kenar başına bayraklar yerine köşe başına işaretleri tahmin eden bir kod çözücü, yapısı gereği parite açısından tutarlı olurdu; bu modelin önceki nesli de böyle çalışıyordu ve bu yüzden ürettiği mesh'ler su geçirmezdi. Bu, çıkarım sırasında yamayabileceğimiz bir şey değil, eğitimde yapılması gereken bir değişiklik. O zamana kadar şerit formülünden iki pratik sonuç çıkarabiliriz. Bir karakter üretiyorsanız, uzuvların gövdeden ayrı durduğu bir poz, uzuvların gövdeye yaslandığı bir poza kıyasla daha temiz bir mesh oluşturur. Böyle bir üreticinin üzerine bir sistem geliştiriyorsanız da bir sayıya güvenmeden önce mesh'lerinizi Euler karakteristiğiyle ölçün ve sonucu kendi gözlerinizle inceleyin. Sayı temiz olduğunu söylüyordu. İki kez yanıldı.
Araçlarla donatılmış kod çözücü, aşama dökümleri, parite analizi ve dolgu artık işlem hattımızın parçası. Böylece delikli sonuçlar üreten bir sonraki modele aynı işlemleri bir hafta yerine dakikalar içinde uygulayabileceğiz.