Tekstur untuk intro 64k: bagaimana hal itu dilakukan hari ini

Artikel ini adalah bagian kedua dari seri H-Immersion kami . Bagian pertama dapat dibaca di sini: Immersion in Immersion .

Saat membuat animasi hanya 64 KB, sulit untuk menggunakan gambar yang sudah jadi. Kami tidak dapat menyimpannya dengan cara tradisional, karena tidak cukup efisien, bahkan jika Anda menerapkan kompresi, misalnya JPEG. Solusi alternatif adalah pembuatan prosedural, yaitu, penulisan kode yang menjelaskan pembuatan gambar selama eksekusi program. Implementasi kami atas solusi ini adalah generator tekstur - bagian mendasar dari rantai alat kami. Dalam posting ini kami akan menjelaskan bagaimana kami mengembangkan dan menggunakannya dalam H - Immersion .


Lampu sorot bawah laut menerangi detail dasar laut.

Versi awal


Generasi tekstur adalah salah satu elemen paling awal dari basis kode kami: tekstur prosedural sudah digunakan dalam pengantar B - Inkubasi pertama kami. Kode terdiri dari serangkaian fungsi yang mengisi, menyaring, mengubah, dan menggabungkan tekstur, serta satu loop besar yang mem-bypass semua tekstur. Fungsi-fungsi ini ditulis dalam C ++ murni, tetapi interaksi C API kemudian ditambahkan sehingga mereka dapat dievaluasi oleh penerjemah C PicoC . Pada saat itu, kami menggunakan PicoC untuk mengurangi waktu yang dibutuhkan oleh setiap iterasi: dengan cara ini kami dapat mengubah dan memuat ulang tekstur selama pelaksanaan program. Beralih ke subset C adalah pengorbanan kecil dibandingkan dengan kenyataan bahwa sekarang kita dapat mengubah kode dan melihat hasilnya segera, tanpa repot dengan menutup, mengkompilasi ulang, dan memuat ulang seluruh demo.


Menggunakan pola sederhana, sedikit suara dan deformasi, kita bisa mendapatkan tekstur kayu bergaya.


Dalam adegan ini dari bengkel F - Felix, berbagai tekstur kayu digunakan.

Untuk beberapa waktu, kami menjelajahi kemampuan generator ini, dan sebagai hasilnya diposting di server web dengan skrip PHP kecil dan antarmuka web sederhana. Kita bisa menulis kode tekstur dalam bidang teks, dan skrip meneruskannya ke generator, yang kemudian membuang hasilnya sebagai file PNG untuk ditampilkan pada halaman. Segera, kami mulai membuat sketsa di tempat kerja saat istirahat makan siang dan berbagi karya kecil kami dengan anggota kelompok lainnya. Interaksi ini memotivasi kami untuk proses kreatif.


Galeri web generator tekstur lama kami. Semua tekstur dapat diedit di browser.

Desain ulang penuh


Untuk waktu yang lama, penghasil tekstur hampir tidak berubah; kami pikir itu baik, dan efektivitas kami berhenti meningkat. Tetapi begitu kami mengetahui bahwa ada banyak seniman di forum Internet yang menunjukkan tekstur yang dihasilkan sepenuhnya secara prosedural, serta mengatur tantangan pada berbagai topik. Konten prosedural pernah menjadi fitur adegan demo, tetapi Allegorithmic , ShaderToy dan alat serupa membuatnya dapat diakses oleh masyarakat umum. Kami tidak memperhatikan hal ini, dan mereka mulai dengan mudah menempatkan kami di tulang belikat. Tidak bisa diterima


Sofa kain Tekstur kain yang sepenuhnya prosedural dibuat di Substance Designer. Dikirim oleh: Imanol Delgado. www.artstation.com/imanoldelgado

gambar

Lantai Hutan . Tekstur tanah hutan sepenuhnya prosedural dibuat oleh Substance Designer. Diposting oleh Daniel Thiger. www.artstation.com/dete

Kami telah lama perlu memikirkan kembali alat kami. Untungnya, bertahun-tahun bekerja dengan generator tekstur yang sama memungkinkan kami untuk mengenali kekurangannya. Selain itu, generator mesh kami yang baru lahir juga memberi tahu kami seperti apa tampilan pipa konten prosedural.

Kesalahan arsitektur yang paling penting adalah implementasi generasi sebagai serangkaian operasi dengan objek tekstur. Dari sudut pandang perspektif tingkat tinggi, ini mungkin pendekatan yang tepat, tetapi dari sudut pandang implementasi, fungsi-fungsi seperti tekstur. Melakukan sesuatu () atau Menggabungkan (tekstur A, tekstur B) memiliki kelemahan serius.

Pertama, gaya OOP mengharuskan Anda untuk mendeklarasikan fungsi-fungsi ini sebagai bagian dari API, tidak peduli sesederhana apa mereka. Ini adalah masalah serius karena tidak skala dengan baik dan, yang lebih penting, menciptakan gesekan yang tidak perlu dalam proses kreatif. Kami tidak ingin mengubah API setiap kali kami perlu mencoba sesuatu yang baru. Ini menyulitkan eksperimen dan membatasi kebebasan kreatif.

Kedua, dalam hal kinerja, pendekatan ini mengharuskan Anda untuk memproses data tekstur dalam siklus sebanyak operasi. Ini tidak akan terlalu penting jika operasi ini mahal sehubungan dengan biaya mengakses fragmen memori yang besar, tetapi biasanya tidak demikian. Dengan pengecualian pada sebagian kecil operasi, misalnya, menghasilkan kebisingan atau pengisian Perlin , mereka pada dasarnya sangat sederhana dan hanya memerlukan beberapa instruksi pada titik tekstur. Artinya, kami mengelak data tekstur untuk melakukan operasi sepele, yang sangat tidak efisien dari sudut pandang caching.

Struktur baru memecahkan masalah-masalah ini melalui reorganisasi logika. Sebagian besar fungsi dalam praktik secara independen melakukan operasi yang sama untuk setiap elemen tekstur. Oleh karena itu, alih-alih menulis fungsi tekstur. Melakukan sesuatu () yang memintas semua elemen, kita dapat menulis tekstur. Menerapkan Fungsi (f) , di mana f (elemen) bekerja hanya untuk elemen tekstur tunggal. Kemudian f (elemen) dapat ditulis sesuai dengan tekstur tertentu.

Ini sepertinya perubahan kecil. Namun, struktur ini menyederhanakan API, membuat kode generasi lebih fleksibel dan ekspresif, lebih ramah cache, dan memungkinkan pemrosesan paralel dengan mudah. Banyak pembaca sudah menyadari bahwa ini pada dasarnya adalah shader. Namun, implementasi yang sebenarnya tetap kode C ++ dieksekusi pada prosesor. Kami masih mempertahankan kemampuan untuk melakukan operasi di luar loop, tetapi gunakan opsi ini hanya bila perlu, misalnya, dengan konvolusi.

Itu:


//     . // API . //    -  API. //      . class ProceduralTexture { void DoSomething(parameters) { for (int i = 0; i < size; ++i) { //   . (*this)[i] = … } } void PerlinNoise(parameters) { … } void Voronoi(parameters) { … } void Filter(parameters) { … } void GenerateNormalMap() { … } }; void GenerateSomeTexture(texture t) { t.PerlinNoise(someParameter); t.Filter(someOtherParameter); … //  .. t.GenerateNormalMap(); } 

Itu menjadi:


 //       . // API . //     . //      . class ProceduralTexture { void ApplyFunction(functionPointer f) { for (int i = 0; i < size; ++i) { //    . (*this)[i] = f((*this)[i]); } } }; void GenerateNormalMap(ProceduralTexture t) { … } void SomeTextureGenerationPass(void* out, PixelInfo in) { result = PerlinNoise(in); result = Filter(result); … //  .. *out = result; } void GenerateSomeTexture(texture t) { t.ApplyFunction(SomeTextureGenerationPass); GenerateNormalMap(t); } 

Paralelisasi


Pembuatan tekstur membutuhkan waktu, dan kandidat yang jelas untuk mengurangi waktu ini adalah eksekusi kode paralel. Paling tidak, Anda dapat mempelajari cara membuat beberapa tekstur sekaligus. Inilah yang kami lakukan untuk bengkel F - Felix , dan ini sangat mengurangi waktu muat.

Namun, ini tidak menghemat waktu di tempat yang paling dibutuhkan. Masih membutuhkan banyak waktu untuk menghasilkan satu tekstur. Ini berlaku untuk perubahan karena kami terus memuat ulang tekstur berulang kali sebelum setiap modifikasi. Sebagai gantinya, lebih baik untuk memparalelkan kode penghasil tekstur internal. Karena sekarang kode pada dasarnya terdiri dari satu fungsi besar yang diterapkan dalam satu loop ke setiap texel, paralelisasi menjadi sederhana dan efisien. Mengurangi biaya percobaan, penyetelan, dan konsep, yang secara langsung memengaruhi proses kreatif.




Ilustrasi sebuah ide yang kami eksplorasi dan dibuang untuk H - Immersion : dekorasi mosaik dengan lapisan orichalcon. Ini ditunjukkan di alat penyuntingan interaktif kami.

Pembuatan sisi GPU


Jika ini masih belum jelas, maka saya akan mengatakan bahwa generasi tekstur sepenuhnya dilakukan dalam CPU. Mungkin beberapa dari Anda membaca kalimat-kalimat ini sekarang dan bingung β€œtetapi mengapa?!”. Tampaknya langkah yang jelas adalah menghasilkan tekstur pada prosesor video. Untuk memulainya, itu akan meningkatkan tingkat generasi dengan urutan besarnya. Jadi mengapa kita tidak menggunakannya?

Alasan utamanya adalah tujuan desain ulang kecil kami adalah tetap menggunakan CPU. Beralih ke GPU akan berarti lebih banyak pekerjaan. Kami harus menyelesaikan masalah tambahan yang masih belum cukup berpengalaman. Bekerja dengan CPU, kami memiliki pemahaman yang jelas tentang apa yang kami inginkan, dan kami tahu cara memperbaiki kesalahan sebelumnya.

Namun, kabar baiknya adalah, berkat struktur baru, bereksperimen dengan GPU sekarang tampaknya cukup sepele. Menguji kombinasi kedua jenis prosesor akan menjadi percobaan yang menarik untuk masa depan.

Generasi tekstur dan bayangan fisik yang akurat


Keterbatasan lain dari desain lama adalah bahwa tekstur dianggap hanya sebagai gambar RGB. Jika kami perlu menghasilkan lebih banyak informasi, katakanlah tekstur difus dan tekstur normals untuk permukaan yang sama, maka tidak ada yang mencegah kami melakukan ini, tetapi API tidak banyak membantu. Ini menjadi sangat penting dalam konteks Fisik Berbasis Shading (PBR).

Dalam pipa tradisional tanpa PBR, tekstur warna biasanya digunakan, di mana banyak informasi dipanggang. Tekstur seperti itu sering mewakili penampilan akhir permukaan: mereka sudah memiliki volume tertentu, retakan menjadi gelap, dan bahkan mungkin ada refleksi pada mereka. Jika beberapa tekstur digunakan secara bersamaan, maka detail skala besar dan kecil biasanya digabungkan untuk menambah peta normal atau reflektifitas permukaan.

Konveyor PBR permukaan biasanya menggunakan beberapa set tekstur yang mewakili nilai fisik daripada hasil artistik yang diinginkan. Tekstur warna difus, yang paling dekat dengan apa yang sering disebut "warna" permukaan, biasanya datar dan tidak menarik. Specular warna ditentukan oleh indeks bias permukaan. Sebagian besar detail dan variabilitas diambil dari tekstur normals dan roughness (kekasaran) (yang seseorang dapat anggap sama, tetapi dengan dua skala yang berbeda). Reflektivitas yang dirasakan dari suatu permukaan menjadi konsekuensi dari tingkat kekasarannya. Pada tahap ini, akan lebih logis untuk berpikir dalam hal bukan material, tetapi material.










Struktur baru memungkinkan kita untuk mendeklarasikan format piksel sembarang untuk tekstur. Setelah menjadikannya bagian dari API, kami mengizinkannya untuk menangani semua kode boilerplate. Setelah mendeklarasikan format piksel, kami dapat fokus pada kode materi iklan tanpa menghabiskan terlalu banyak upaya memproses data ini. Pada saat dijalankan, itu akan menghasilkan beberapa tekstur dan secara transparan mentransfernya ke GPU.

Dalam beberapa pipa PBR, warna difus dan specular tidak ditransmisikan secara langsung. Sebagai gantinya, parameter "warna dasar" dan "logam" digunakan, yang memiliki kelebihan dan kekurangan. Dalam H - Immersion, kami menggunakan model specular difus +, dan material biasanya terdiri dari lima lapisan:

  1. Warna difus (RGB; 0: Vantablack ; 1: salju segar ).
  2. Warna specular (RGB: fraksi cahaya dipantulkan pada 90 Β°, juga dikenal sebagai F0 atau R0 ).
  3. Kekasaran (A; 0: sangat halus; 1: seperti karet).
  4. Normal (XYZ; vektor satuan).
  5. Elevation of terrain (A; digunakan untuk pemetaan oklusi paralaks).

Saat digunakan, informasi emisi cahaya ditambahkan langsung ke shader. Kami tidak merasa perlu untuk memiliki oklusi ambien, karena di sebagian besar adegan tidak ada pencahayaan sama sekali. Namun, saya tidak akan terkejut bahwa kita akan memiliki lapisan tambahan atau jenis informasi lainnya, misalnya, anisotropi atau opacity.



Gambar di atas menunjukkan percobaan terbaru dengan menghasilkan oklusi ambien lokal berdasarkan ketinggian. Untuk setiap arah, kita melalui jarak yang telah ditentukan dan mempertahankan kemiringan terbesar (perbedaan ketinggian dibagi dengan jarak). Kemudian kami menghitung oklusi dari kemiringan rata-rata.

Kendala dan pekerjaan di masa depan


Seperti yang Anda lihat, struktur baru telah menjadi peningkatan besar dari yang lama. Selain itu, ia mendorong ekspresi kreatif. Namun, dia masih memiliki keterbatasan yang ingin kita hilangkan di masa depan.

Sebagai contoh, meskipun tidak ada masalah dalam pengantar ini, kami perhatikan bahwa alokasi memori bisa menjadi kendala. Saat menghasilkan tekstur, satu array nilai float digunakan. Dengan tekstur besar dengan banyak lapisan, Anda dapat dengan cepat menemukan masalah dengan alokasi memori. Ada berbagai cara untuk menyelesaikannya, tetapi mereka semua memiliki kekurangan. Sebagai contoh, kita dapat menghasilkan tekstur ubin per ubin, sementara skalabilitas akan lebih baik, namun implementasi beberapa operasi, seperti konvolusi, menjadi kurang jelas.

Selain itu, dalam artikel ini, terlepas dari kata "bahan" yang digunakan, kami hanya berbicara tentang tekstur, tetapi tidak tentang shader. Namun, penggunaan material juga harus mengarah pada shader. Kontradiksi ini mencerminkan keterbatasan struktur yang ada: pembentukan tekstur dan bayangan adalah dua bagian terpisah yang dipisahkan oleh jembatan. Kami mencoba membuatnya mudah untuk menyeberangi jembatan ini, tetapi sebenarnya kami ingin bagian ini menjadi satu. Misalnya, jika suatu material memiliki parameter statis dan dinamis, maka kami ingin menggambarkannya di satu tempat. Ini adalah topik yang kompleks dan kami belum tahu apakah ada solusi yang baik, tapi jangan maju dulu.

gambar

Eksperimen untuk membuat tekstur kain yang mirip dengan karya Imadol Delgado yang ditunjukkan di atas.

Source: https://habr.com/ru/post/id419007/


All Articles