Skip to content

Dari mana asal lubang pada mesh yang dihasilkan ​

Oleh Oleg Sidorkin, CTO dan Salah Satu Pendiri Cinevva

Pangkal sayap burung yang dihasilkan sebelum dan sesudah perbaikan: lipatan abu-abu dengan celah-celah hijau terang di sebelah kiri, serta lipatan sayap ke tubuh yang bersih di sebelah kanan
Gambar yang sama, generator yang sama, pengaturan yang sama. Sebelah kiri adalah hasil yang digunakan hingga minggu ini. Sebelah kanan adalah hasil yang digunakan sekarang.

Saya menghasilkan seekor burung dengan TRELLIS.2, memasang rig, lalu begitu animasinya diputar, muncul lubang di bawah kedua sayapnya. Ada celah-celah terang yang memungkinkan kita melihat langsung menembus tubuhnya. Proses rigging menjadi tersangka pertama, dan memang memiliki masalah tersendiri, tetapi lubang-lubang itu sudah ada pada mesh sebelum satu pun bone menyentuhnya. Artikel ini membahas investigasi yang kemudian kami lakukan: apa sebenarnya lubang-lubang tersebut, sembilan hal yang gagal memperbaikinya beserta alasannya, dan satu hal yang berhasil. Saya sengaja menyertakan semua jalan buntunya. Kebanyakan merupakan solusi yang paling masuk akal untuk dicoba, dan masing-masing mengajarkan sesuatu yang kemudian menjadi dasar perbaikan akhir kami.

Langkah pertama: sepakati apa yang dimaksud dengan lubang ​

Pemeriksa mesh kami melaporkan nol boundary edge, dan pengujian paritas ray menyatakan bahwa permukaannya tertutup. Jadi, saya memberi tahu tim bahwa mesh tersebut bersih. Ternyata tidak, dan orang yang menandai celah-celah itu dengan warna merah memang benar untuk menyanggahnya. Sebuah mesh dapat tertutup tetapi masih memiliki terowongan. Satu-satunya ukuran yang dapat mendeteksinya adalah karakteristik Euler, V dikurangi E ditambah F, yang menghasilkan jumlah handle per shell. Burung tersebut memiliki 69 handle. Setiap dinding dalam hasil ekspor berlapis ganda, sehingga celah pada dinding luar yang terhubung ke rongga di belakangnya merupakan handle, bukan bukaan, dan metode apa pun yang mencari edge terbuka tidak akan pernah mendeteksinya.

Pelajaran kedua, yang lebih memalukan: saya sempat memeriksa piksel-piksel gelap untuk mencari lubang, karena lubang berwarna gelap, sampai seseorang menyuruh saya melihat gambarnya terlebih dahulu. Lubang-lubang itu terang. Yang terlihat adalah celah-celah tekstur hijau dari dinding seberang yang muncul melalui lipatan abu-abu. Algoritma yang belum diarahkan ke sasaran yang tepat tidak akan memberi tahu Anda apa pun, dan cara termurah untuk mengarahkannya adalah dengan melihat.

Langkah kedua: temukan tahap yang menciptakannya ​

TRELLIS.2 membangun mesh dalam tiga langkah. Decoder menghasilkan permukaan mentah pada resolusi 1024 pangkat tiga. Proses dual contouring narrow-band kemudian melakukan remeshing. Decimator menurunkannya menjadi seratus ribu face, lalu tekstur di-bake. Kami mengekspor geometri setelah setiap langkah dan menembakkan kumpulan ray yang sama melalui pangkal sayap pada setiap tahap.

Masalahnya bermula pada permukaan mentah. Permukaan itu berupa kumpulan sheet terbuka: 266.242 boundary loop, yang sebagian besar hanya berupa satu quad yang hilang, serta 65.371 edge non-manifold. Pada pangkal sayap, sepertiga ray pengujian menembus lurus. Proses pengisian lubang berikutnya nyaris tidak menutup apa pun, hanya 144 face dari lima juta. Remesher bekerja menggunakan unsigned distance field karena memang harus demikian. Tidak ada konsep bagian dalam atau luar yang bisa digunakannya, jadi remesher membungkus setiap sheet dengan shell offset tipis. Itulah sebabnya setiap dinding berlapis ganda, terpisah sekitar empat voxel, dan begitulah setiap garis berpori berubah menjadi lipatan yang memiliki celah. Decimation kemudian memperburuknya secara acak: mesh mentah yang sama, setelah di-decimate dua kali, menghasilkan 69 dan 107 terowongan.

Burung dengan peta panas kepadatan edge terbuka: kerah leher, kedua pangkal sayap, pinggul, dan ujung bulu ekor menyala, sedangkan tubuh dan kepala bersih
Kepadatan edge terbuka dalam hasil ekstraksi mentah. Area yang menyala hanya tempat bagian-bagian saling bertemu: kerah leher, pangkal sayap, pinggul, dan ujung ekor. Tubuh dan kepalanya bersih.

Peta tersebut menjadi petunjuk nyata pertama. Porositasnya tidak muncul secara acak. Porositas berada di setiap sambungan antarbagian dan tidak ada di tempat lain.

Hal-hal yang tidak berhasil ​

Kami mencobanya kurang lebih berdasarkan urutan solusi yang paling jelas.

percobaanhasil
resolusi 1536, bukan 1024lebih buruk pada tiga dari lima model pengujian. Resolusi yang lebih tinggi menangkap lebih banyak kontak tipis, dan masing-masing menjadi sambungan baru
rangkaian cleanup bawaanmenghasilkan lebih banyak edge non-manifold, bukan lebih sedikit
mengisi setiap lubang kecil dengan MeshLib266 ribu loop terisi, tetapi pangkal sayap tidak berubah. Skin di sana memang tidak pernah dihasilkan, jadi tidak ada loop yang dapat diisi
signed remesh dari winding numbermemecah mesh menjadi ribuan shell. Hasil mentah tidak memiliki interior yang dapat diberi sign
contouring band yang lebih lebar, 2 dan 3 voxeljumlah terowongan keseluruhan berkurang, tetapi celahnya justru melebar, bukan menyempit. Terowongan kecil bergabung menjadi terowongan besar
morphological closing pada struktur voxelmemadatkan shell, lalu decoder menghasilkan permukaan di dalam setiap voxel interior, face bertambah 4x, 969 terowongan
voxel remesh global pada mesh akhirgenus 0 dan celah tertutup. Namun, setiap tepi bulu juga menjadi lebih halus. Langsung ditolak setelah dilihat, dan itu keputusan yang benar
menggeser ambang crossing decoderedge terbuka turun dari 65 ribu menjadi 58 ribu. Hanya noise
melengkapi voxel tetangga yang hilang560 kasus di seluruh burung. Hanya noise
Empat kolom: burung asli dan tiga versi hasil voxel remesh dengan radius yang semakin besar, pangkal sayap, perbesaran sepuluh kali, irisan penampang, seluruh burung, dan tampilan dari bawah
Global closing. Kolom ketiga menutup celah dan menjaga jari-jari kaki tetap terpisah, tetapi juga membulatkan setiap lekukan pada burung. Seperti inilah geometri yang terlihat "buram".

Global closing layak dibahas lebih jauh karena itulah hal pertama yang mungkin dilakukan seorang pemodel: jalankan voxel remesh dengan ukuran sedikit lebih besar daripada celah. Secara topologi, cara ini berhasil. Namun, cara ini juga menghasilkan representasi model yang tidak jujur. Generator menghasilkan tepi bulu yang tajam, dan kami akan mengorbankannya demi menutup pangkal sayap. Dua dari sembilan percobaan berhasil menurunkan jumlah terowongan, dan keduanya langsung ditolak saat seseorang melihat burungnya, bukan hanya angkanya.

Membaca kode ​

Pada tahap itu, satu-satunya tindakan yang jujur adalah berhenti memperlakukan decoder sebagai kotak hitam dan membaca bagaimana hasilnya diubah menjadi mesh. TRELLIS.2 menggunakan representasi bernama O-Voxel, dan kode ekstraksinya singkat. Setiap voxel aktif menyimpan satu dual vertex dan tiga logit, satu untuk setiap grid edge yang keluar dari sudutnya, dan masing-masing berarti "permukaan melintasi edge ini." Logit di atas nol menghasilkan quad yang menghubungkan empat voxel di sekitar edge tersebut. Jika salah satu dari keempatnya hilang, quad itu dibuang tanpa peringatan. Hanya itu. Tidak ada sign field, occupancy, maupun konsep bagian dalam dan luar di mana pun dalam model.

Kami memodifikasi decoder agar mengekspor logit-logit tersebut sebelum penerapan ambang, pada burung yang sama, lalu menghitungnya.

Tetangga yang hilang: 560 dari 2,7 juta voxel. Logit yang berada di sekitar nol: 8.600 dari 8 juta. Keduanya bukan sumber masalah. Masalahnya terlihat saat kita memeriksa empat edge di sekeliling setiap grid face. Permukaan tertutup melintasi batas sebuah face dalam jumlah genap. Pada 126.542 face, atau 2,3% di antaranya, keempat flag tidak konsisten, dan setiap face berjumlah ganjil pasti merupakan edge terbuka atau non-manifold. Ketidakkonsistenan itu juga terjadi dengan keyakinan tinggi. Satu-satunya flag pada face dengan hitungan satu memiliki logit +10, sedangkan tetangganya memiliki logit -4. Biaya median untuk membalik satu flag dan memulihkan paritas adalah 2,3 logit. Ini bukan sekadar ambang yang sedikit meleset. Model yakin akan keberadaan permukaan yang tidak mungkin tertutup.

Tiga kolom pada latar belakang gelap: burung hasil ekspor, hasil mentah decoder, dan versi dengan sign yang diperbaiki. Pada perbesaran sepuluh dan tiga puluh kali, sambungan sayap tampak seperti garis bergerigi yang tercabik pada kedua versi mentah
Hasil mentah decoder pada pangkal sayap; area gelap berarti tembus pandang. Sambungan tersebut bukan kehilangan quad. Bentuknya menyerupai sisir.

Kemudian kami mengukur garis itu sendiri, tepat pada titik permukaan yang terus diamati kamera tetap kami sepanjang minggu. Dalam jarak dua belas voxel dari titik tersebut, terdapat voxel aktif sebanyak 2,1 kali jumlah yang dibutuhkan oleh satu sheet datar. Sebagai perbandingan, pada bagian dada nilainya 1,2. Bagian bawah sayap dan tubuh merupakan dua skin yang berada dalam satu lapisan voxel. Representasi ini hanya memiliki satu vertex per voxel, sehingga tidak dapat menampung keduanya. Selama pelatihan, ground truth untuk voxel semacam itu merupakan rata-rata least-squares dari kedua skin dengan flag "any crossing", yang juga tidak konsisten. Jadi, model telah belajar dengan akurat untuk menghasilkan bentuk seperti sisir di sana. Remesher membungkus bentuk seperti sisir itu menjadi slab, dan slab tersebut memiliki terowongan.

Lebar garisnya dapat dijelaskan oleh geometri: kira-kira satu voxel dibagi tangen sudut kontak. Dua permukaan yang bertemu pada sudut sembilan puluh derajat menghasilkan lekukan selebar satu voxel dan tidak menimbulkan masalah. Sayap yang menempel datar pada tubuh, bulu yang menumpuk di atas bulu lain, dan jari kaki yang menyentuh tanah—semuanya menghasilkan garis selebar sepuluh atau lima belas voxel. Itulah penjelasan untuk peta porositas tersebut.

Saya memang mencoba perbaikan yang secara teori paling tepat, yaitu menyimpulkan sign dalam/luar untuk setiap sudut yang paling mampu menjelaskan flag, lalu menurunkan ulang flag tersebut. Cara ini tidak mungkin berhasil. Sign field hanya ada pada shell selebar satu voxel yang dihasilkan decoder, dan setiap perluasan ke luar shell tersebut tumbuh tak terkendali. Dua belas putaran proses itu menggandakan jumlah voxel dengan data sampah.

Yang akan dilakukan seorang pemodel ​

Seorang pemodel tidak akan pernah membiarkan dua skin hanya berjarak setipis rambut. Mereka akan melakukan union antara sayap dan tubuh, lalu membiarkannya membentuk sebuah lekukan. Global closing melakukan hal itu di seluruh bagian, dan itulah masalahnya. Jadi, lakukan hanya di tempat kedua skin tersebut berada.

Logika detektornya hanya terdiri dari satu gagasan. Hitung jumlah tetangga aktif setiap voxel dalam radius empat voxel. Pada satu sheet, jumlah tersebut sama di mana-mana dan setara dengan mediannya. Di tempat dua sheet berbagi lapisan yang sama, jumlahnya kira-kira dua kali lipat. Tandai semua yang berada di atas 1,5 kali median, perluas kumpulan yang ditandai sebanyak satu voxel, lalu tempatkan kubus satuan pada masing-masing titik. Ruang di antara kedua skin menjadi padat, proses contouring hanya melihat satu dinding, dan bagian lain pada model tidak tersentuh.

Potongan di sekitar pangkal sayap yang di-contour dengan tiga cara: sebagaimana adanya, dengan zona dua sheet yang diisi pada radius tiga, dan pada radius empat. Titik-titik merah menandai voxel yang diisi di sepanjang sambungan. Sambungan bergerigi berubah menjadi lekukan halus
Pembuktian konsep pada sebuah potongan sebelum menyentuh pipeline. Warna merah menandai voxel yang diisi. Sambungan bergerigi berubah menjadi lekukan, dan bulu-bulu di sekitarnya tidak bergeser.

Kami terlebih dahulu mengujinya pada sebuah potongan, menggunakan MeshLib sebagai pengganti sementara untuk GPU contouring. Lima puluh tiga shell dan sembilan belas handle berubah menjadi dua shell dan lima handle. Kemudian kami memasukkannya ke pipeline sebenarnya, di antara decoder dan ekspor, lalu menjalankan burung yang sama melalui remesher dan proses bake tekstur yang sebenarnya.

Empat kolom, pertama dengan tekstur, kemudian geometri polos, lalu penampang: versi asli, hasil decoder yang sama dan diekspor sebagaimana adanya, serta pengisian dua sheet pada radius tiga dan empat. Lipatan abu-abu dan celah hijau menghilang pada dua kolom terakhir
Pipeline sebenarnya. Kolom satu dan dua berasal dari hasil decoder yang sama, dengan dan tanpa decimation acak. Kolom tiga dan empat menggunakan pengisian.
terowonganedge non-manifoldlipatan pangkal sayaplubang di punggung
sebagaimana adanya79,5318adaada
pengisian, radius 384,597hilanghilang
pengisian, radius 47238hilanghilang

Lipatannya hilang. Lubang kedua di punggung burung, pada sambungan yang sama tetapi terlihat dari belakang dan bahkan tidak sedang saya telusuri, juga hilang. Bulu-bulu tetap terpisah, jari-jari kaki tetap terpisah, dan tekstur dapat di-bake secara normal karena kubus-kubus tersebut berada di dalam shell, tempat volume atribut sudah memiliki data. Proses ini menambah enam detik pada pekerjaan berdurasi empat menit. Sekarang fitur tersebut aktif secara default dengan radius empat.

Seluruh burung dari depan, belakang, dari bawah dengan tekstur, dan dari bawah sebagai geometri polos, dalam empat kolom: versi asli, sebagaimana adanya, pengisian radius tiga, dan pengisian radius empat
Seluruh burung. Lubang putih di punggung pada kolom satu dan dua adalah sambungan yang sama jika dilihat dari belakang. Semua bagian yang sudah dihasilkan dengan benar oleh generator tetap dipertahankan pada kolom tiga dan empat.

Yang tidak diperbaiki oleh metode ini, dan pelajaran yang dapat diambil ​

Jumlah terowongan hampir tidak berubah, dan saya ingin menjelaskan alasannya secara jujur. Terowongan yang tersisa berada di ujung bulu dan ujung jari kaki, tempat bagian tipis berakhir dan dinding berlapis ganda menjepit hingga tertutup. Ukurannya kecil, tidak terlihat, dan disebabkan oleh mekanisme yang berbeda. Pengisian ini menargetkan kontak antarbagian, yaitu lokasi munculnya lubang-lubang yang terlihat. Perbaikan yang sebenarnya harus dilakukan di hulu. Dekoder yang memprediksi tanda per sudut alih-alih flag per rusuk akan konsisten secara paritas sejak awal, seperti cara kerja generasi sebelumnya dari model ini dan alasan mesh yang dihasilkannya kedap air. Itu merupakan perubahan pada pelatihan, bukan sesuatu yang bisa kami tambal saat inferensi. Sampai saat itu, ada dua hal praktis yang dapat disimpulkan dari rumus garis tersebut. Jika Anda membuat karakter, pose dengan anggota tubuh yang tidak menempel pada badan akan menghasilkan mesh yang lebih bersih dibandingkan pose dengan anggota tubuh yang menempel rata pada badan. Dan jika Anda mengembangkan sesuatu di atas generator seperti ini, ukur mesh Anda menggunakan karakteristik Euler dan periksa hasilnya dengan mata kepala sendiri sebelum memercayai angka. Angkanya menyatakan bersih. Ternyata salah dua kali.

Dekoder berinstrumen, dump setiap tahap, analisis paritas, dan proses penambalan kini semuanya sudah ada dalam pipeline kami, sehingga model berikutnya yang menghasilkan lubang bisa ditangani dengan cara yang sama dalam hitungan menit, bukan seminggu.