Dalam sistem material berbasis grafik, bagaimana saya bisa mendukung berbagai tipe input dan output?

11

masukkan deskripsi gambar di sini

Saya mencoba membungkus kepala saya di sekitar bagaimana sistem material seperti ini , ini diimplementasikan. Sistem seperti grafik yang kuat dan ramah pengguna ini nampaknya relatif umum sebagai metode yang memungkinkan programmer dan non-programmer untuk dengan cepat membuat shader. Namun, dari pengalaman saya yang relatif terbatas dengan pemrograman grafis, saya tidak sepenuhnya yakin bagaimana cara kerjanya.


Latar Belakang:

Jadi, ketika saya telah memprogram sistem rendering OpenGL sederhana sebelumnya, saya biasanya membuat kelas Material yang memuat, mengkompilasi, dan menghubungkan shader dari file GLSL statis yang saya buat secara manual. Saya juga biasanya membuat kelas ini sebagai pembungkus sederhana untuk mengakses variabel seragam GLSL. Sebagai contoh sederhana, bayangkan saya memiliki vertex shader dasar dan fragmen shader, dengan Texture2D ekstra seragam untuk melewatkan tekstur. Kelas Material saya hanya akan memuat dan mengkompilasi dua shader menjadi sebuah materi, dan sejak saat itu akan mengekspos antarmuka sederhana untuk membaca / menulis seragam Texture2D dari shader itu.

Untuk membuat sistem ini sedikit lebih fleksibel, saya biasanya menulisnya dengan cara yang memungkinkan saya mencoba untuk lulus seragam dari semua nama / tipe [yaitu: SetUniform_Vec4 ("AmbientColor", colorVec4); yang akan mengatur seragam AmbientColor ke vektor 4d tertentu yang disebut "colorVec4" jika seragam itu ada di material.] .

class Material
{
    private:
       int shaderID;
       string vertShaderPath;
       string fragSahderPath;

       void loadShaderFiles(); //load shaders from files at internal paths.
       void buildMaterial(); //link, compile, buffer with OpenGL, etc.      

    public:
        void SetGenericUniform( string uniformName, int param );
        void SetGenericUniform( string uniformName, float param );
        void SetGenericUniform( string uniformName, vec4 param );
        //overrides for various types, etc...

        int GetUniform( string uniformName );
        float GetUniform( string uniformName );
        vec4 GetUniform( string uniformName );
        //etc...

        //ctor, dtor, etc., omitted for clarity..
}

Ini berfungsi tetapi rasanya seperti sistem yang buruk karena fakta bahwa klien dari kelas Material harus mengakses seragam hanya berdasarkan keyakinan - pengguna harus agak menyadari seragam yang ada di setiap objek material karena mereka dipaksa untuk berikan nama GLSL mereka. Ini bukan masalah besar ketika hanya 1-2 orang yang bekerja dengan sistem, tapi saya tidak bisa membayangkan sistem ini akan berskala sangat baik sekali, dan sebelum saya melakukan upaya berikutnya dalam pemrograman sistem rendering OpenGL, saya ingin naik level sedikit.


Pertanyaan:

Di situlah saya sejauh ini, jadi saya telah mencoba mempelajari bagaimana mesin rendering lain menangani sistem material mereka.

Pendekatan berbasis simpul ini sangat bagus dan tampaknya menjadi sistem yang sangat umum untuk menciptakan sistem material yang ramah pengguna dalam mesin dan peralatan modern. Dari apa yang saya tahu mereka didasarkan pada struktur data grafik di mana setiap node mewakili beberapa aspek shader dari materi Anda dan setiap jalur mewakili beberapa jenis hubungan di antara mereka.

Dari apa yang dapat saya katakan, menerapkan sistem semacam itu akan sesederhana kelas MaterialNode dengan berbagai subclass (TextureNode, FloatNode, LerpNode, dll.). Di mana setiap subclass MaterialNode akan memiliki MaterialConnections.

class MaterialConnection
{
    MatNode_Out * fromNode;
    MatNode_In * toNode;
}

class LerpNode : MaterialNode
{
    MatNode_In x;
    MatNode_In y;
    MatNode_In alpha;

    MatNode_Out result;
}

Itu ide yang sangat mendasar , tetapi saya sedikit tidak yakin tentang bagaimana beberapa aspek dari sistem ini akan bekerja:

1.) Jika Anda melihat berbagai 'Ekspresi Material' (node) yang digunakan Unreal Engine 4 , Anda akan melihat bahwa mereka masing-masing memiliki koneksi input dan output dari berbagai jenis. Beberapa node keluaran mengapung, beberapa vektor output2, beberapa vektor output4, dll. Bagaimana saya dapat meningkatkan node dan koneksi di atas sehingga mereka dapat mendukung berbagai jenis input dan output? Apakah subkelas MatNode_Out dengan MatNode_Out_Float dan MatNode_Out_Vec4 (dan seterusnya) menjadi pilihan yang bijak?

2.) Akhirnya, bagaimana sistem semacam ini berhubungan dengan shader GLSL? Melihat lagi UE4 (dan juga untuk sistem lain yang terhubung di atas), pengguna diharuskan untuk akhirnya menyambungkan beberapa node material ke node besar dengan berbagai parameter yang mewakili parameter shader (warna dasar, metalness, gloss, emissiveness, dll.) . Asumsi awal saya adalah bahwa UE4 memiliki semacam 'master shader' dengan kode yang seragam dengan berbagai seragam, dan segala sesuatu yang dilakukan pengguna dalam 'materi' mereka hanya diteruskan ke 'master shader' ketika mereka memasukkan node mereka ke dalam ' master node '.

Namun, dokumentasi UE4 menyatakan:

"Setiap node berisi potongan kode HLSL, yang ditunjuk untuk melakukan tugas tertentu. Ini berarti bahwa ketika Anda membangun sebuah Bahan, Anda membuat kode HLSL melalui skrip visual."

Jika itu benar, apakah sistem ini menghasilkan skrip shader nyata? Bagaimana tepatnya cara kerjanya?

MrKatSwordfish
sumber
1
Terkait dengan pertanyaan Anda: gameangst.com/?p=441
glampert

Jawaban:

10

Saya akan mencoba menjawab yang terbaik dari pengetahuan saya, dengan sedikit pengetahuan tentang kasus spesifik UE4, tetapi lebih pada teknik umum.

Materi berbasis grafik adalah pemrograman sebanyak menulis kode sendiri. Rasanya tidak seperti itu untuk orang-orang tanpa latar belakang kode, membuatnya tampak lebih mudah. Jadi, ketika seorang desainer menautkan sebuah simpul "Tambah", ia pada dasarnya menulis add (value1, value2) dan menghubungkan output ke sesuatu yang lain. Ini adalah apa yang mereka maksudkan bahwa setiap node akan menghasilkan kode HLSL, baik itu panggilan fungsi atau hanya instruksi sederhana.

Pada akhirnya, menggunakan grafik material seperti memprogram shader mentah dengan pustaka fungsi yang telah ditentukan sebelumnya yang melakukan beberapa hal berguna yang umum, dan itulah yang dilakukan UE4. Ia memiliki pustaka kode shader yang akan diambil oleh compiler shader dan disuntikkan ke sumber shader terakhir jika ada.

Dalam kasus UE4, jika mereka mengklaim dikonversi ke HLSL, saya menganggap mereka menggunakan alat konverter yang mampu mengubah kode byte HLSL menjadi kode byte GLSL, sehingga dapat digunakan pada platform GL. Tetapi perpustakaan lain hanya memiliki beberapa kompiler shader, yang akan membaca grafik dan secara langsung menghasilkan sumber bahasa shading yang dibutuhkan.

Grafik materi juga merupakan cara yang bagus untuk abstrak dari platform spesifik dan fokus pada apa yang penting dari sudut pandang seni. Karena tidak terikat pada bahasa dan tingkat yang jauh lebih tinggi, lebih mudah untuk mengoptimalkan platform target dan secara dinamis menyuntikkan kode lain seperti penanganan cahaya ke shader.

1) Sekarang untuk menjawab pertanyaan Anda secara lebih langsung, Anda harus memiliki pendekatan berbasis data untuk merancang sistem seperti itu. Temukan format datar yang dapat didefinisikan dalam struktur yang sangat sederhana, dan bahkan didefinisikan dalam file teks. Pada dasarnya, setiap grafik harus berupa array node, dengan tipe, serangkaian input dan output, dan masing-masing bidang ini harus memiliki link_id lokal untuk memastikan koneksi grafik tidak ambigu. Selain itu, masing-masing bidang ini dapat memiliki konfigurasi tambahan untuk apa yang didukung bidang tersebut (misalnya, kisaran tipe data apa yang didukung).

Dengan pendekatan ini, Anda bisa dengan mudah mendefinisikan bidang simpul menjadi (float | double) dan membiarkannya menyimpulkan tipe dari koneksi, atau memaksa tipe ke dalamnya, tanpa hierarki kelas atau rekayasa ulang. Terserah Anda untuk merancang struktur data grafik ini sekokoh atau sefleksibel yang Anda inginkan. Yang Anda butuhkan adalah memiliki informasi yang cukup sehingga pembuat kode tidak akan memiliki ambiguitas dan karena itu berpotensi menangani kesalahan apa yang ingin Anda lakukan. Yang penting adalah bahwa pada tingkat struktur data dasar, Anda tetap fleksibel dan fokus pada penyelesaian tugas menentukan materi saja.

Ketika saya berkata, "define a material" Saya secara khusus mengacu pada mendefinisikan permukaan mesh, di luar apa yang disediakan oleh geometri itu sendiri. Ini termasuk menggunakan atribut vertex tambahan untuk mengonfigurasi aspek permukaan, menambahkan perpindahan padanya dengan peta ketinggian, mengganggu normals dengan normals per-piksel, mengubah parameter berbasis fisik, mengubah BRDF, dan sebagainya. Anda tidak ingin mendeskripsikan hal lain seperti HDR, tonemapping, animasi skinning, handling ringan atau banyak hal lain yang dilakukan dalam shader.

2) Terserah pembuat shader renderer untuk melintasi struktur data ini dan dengan membaca informasinya, merakit serangkaian variabel dan menghubungkannya bersama-sama menggunakan fungsi premade dan menyuntikkan kode yang menghitung pencahayaan dan efek lainnya. Hanya ingat bahwa shader berbeda tidak hanya dari grafik API yang berbeda, tetapi juga antara penyaji yang berbeda (penangguhan rendering vs penyaji berbasis vs penyaji semua memerlukan pengubah shader yang berbeda untuk bekerja), dan dengan sistem bahan seperti ini, Anda dapat mengambil abstrak dari yang buruk. lapisan tingkat rendah, dan fokus hanya pada menggambarkan permukaan. '

Untuk UE4, mereka membuat daftar hal-hal untuk simpul keluaran akhir yang Anda sebutkan, yang menurut mereka menggambarkan 99% permukaan di game lama dan modern. Mereka mengembangkan set parameter ini selama beberapa dekade dan membuktikannya dengan jumlah game gila yang diproduksi mesin Unreal sejauh ini. Karena itu Anda akan baik-baik saja jika Anda melakukan hal-hal dengan cara yang sama seperti tidak nyata.

Untuk menyelesaikannya, saya sarankan file material hanya untuk menangani setiap grafik. Selama pengembangan, mungkin berisi format berbasis teks untuk debug dan kemudian dikemas atau dikompilasi ke biner untuk rilis. Setiap .material akan disusun oleh N node dan koneksi N, sangat mirip dengan database SQL. Setiap node akan memiliki bidang N, dengan nama dan beberapa bendera untuk jenis yang diterima, jika input atau ouput, jika jenisnya disimpulkan, dll. Struktur data runtime untuk menahan materi yang dimuat akan sama rata dan sederhana, sehingga Editor dapat dengan mudah mengadaptasinya dan menyimpan kembali ke file.

Dan kemudian meninggalkan tugas berat yang sebenarnya untuk generasi shader terakhir, yang benar-benar bagian paling sulit untuk dilakukan. Bagian yang indah adalah bahwa materi Anda tetap agnostik ke platform rendering, secara teori itu akan bekerja dengan teknik rendering dan API apa pun selama Anda mewakili materi dalam bahasa bayangan yang sesuai.

Beri tahu saya jika Anda membutuhkan detail tambahan atau perbaikan apa pun dalam jawaban saya, saya kehilangan gambaran umum atas semua teks.

Grimshaw
sumber
Saya tidak bisa cukup berterima kasih kepada Anda karena menulis jawaban yang rumit dan luar biasa ini. Aku merasa punya ide bagus ke mana aku harus pergi dari sini! Terima kasih!
MrKatSwordfish
1
Tidak masalah kawan, jangan ragu untuk mengirim pesan kepada saya jika Anda membutuhkan bantuan lebih lanjut. Saya sebenarnya mengerjakan sesuatu yang setara untuk alat saya sendiri, jadi jika Anda ingin bertukar pikiran, jadilah tamu saya! Selamat sore: D
Grimshaw