Toon / cel shading dengan lebar garis variabel?

15

Saya melihat beberapa pendekatan luas di luar sana untuk melakukan cel shading:

  1. Duplikasi & pembesaran model dengan membalik normals (bukan pilihan bagi saya)
  2. Sobel filter / fragmen shader mendekati ke deteksi tepi
  3. Buffer stensil mendekati deteksi tepi
  4. Pendekatan shader geometri (atau simpul) yang menghitung normals muka dan tepi

Apakah saya benar dalam mengasumsikan pendekatan geometri-sentris memberikan jumlah kontrol terbesar atas pencahayaan dan ketebalan garis, juga misalnya. untuk medan di mana Anda mungkin melihat garis siluet bukit bergabung secara bertahap menjadi dataran?

Bagaimana jika saya tidak membutuhkan pencahayaan piksel pada permukaan medan saya? (Dan saya mungkin tidak akan seperti saya berencana untuk menggunakan pencahayaan / bayangan berbasis vertex atau texturemap berbasis). Apakah saya kemudian akan lebih baik bertahan dengan pendekatan tipe geometri, atau pergi untuk ruang layar / pendekatan fragmen sebagai gantinya untuk menjaga hal-hal sederhana? Jika demikian, bagaimana saya akan mendapatkan "tinta" dari bukit-bukit dalam siluet mesh, daripada hanya garis besar seluruh mesh (tanpa "tinta" detail di dalam garis besar itu? ( Kontur sugestif AKA , lipatan ).

Terakhir, apakah mungkin untuk meniru dengan murah pendekatan flipped-normals, menggunakan geometri shader? Kekhawatiran saya dengan ini adalah bahwa saya pasti bisa menduplikasi setiap titik tunggal dan skala ini sesuai, tetapi bagaimana saya akan mendekati membalik normal, dan pewarnaan yang berbeda dalam fragmen shader?

Yang saya inginkan - ketebalan garis yang bervariasi dengan garis-garis intrusif di dalam siluet ...

masukkan deskripsi gambar di sini

Apa yang tidak saya inginkan ...

masukkan deskripsi gambar di sini


EDIT: Penelitian lebih lanjut telah muncul berikut ...

Karena saya memiliki jumlah titik besar di medan, bahkan mempertimbangkan LoD berbasis jarak, tidak membalik-normals atau pendekatan berbasis geometri shader (bahkan dengan pemusnahan frustum) akan menjadi pilihan yang masuk akal karena kompleksitas komputasi belaka yang terlibat dalam duplikasi & penskalaan semua simpul yang diunggah.

Mempertimbangkan bahwa saya tidak memerlukan pencahayaan per-pixel dalam bentuk bayangan warna solid pada permukaan medan, itu juga menjadi kurang bijaksana untuk mempertimbangkan pendekatan berbasis wajah-normal - jika tidak persyaratan untuk pencahayaan permukaan yang benar - karena ini secara alami cukup mahal untuk dihitung. Namun benar bahwa mereka memberikan tingkat kontrol terbaik; misalnya, kemampuan untuk menaungi tepi menggunakan pukulan "artistik": Cantik, tetapi sekali lagi, tidak benar-benar layak untuk lingkungan permainan yang sangat kompleks.

Buffer stensil adalah sesuatu yang saya lebih suka hindari karena saya lebih suka melakukan semua pekerjaan di shader. (Contoh di atas dengan garis merah dilakukan dengan buffer stensil - sekolah tua.)

Ini meninggalkan fragmen shader pendekatan gambar-ruang. Kompleksitas komputasi dikurangi menjadi jumlah fragmen daripada jumlah simpul (dalam kasus saya, ini adalah operasi 10-100x lebih sedikit daripada yang harus saya lakukan dalam geometri shader). Ini membutuhkan lebih dari satu render pass untuk menghasilkan buffer-g (terdiri dari buffer normal dan juga opsional buffer kedalaman) di mana kita dapat menerapkan filter diskontinuitas (mis. Operator Sobel). Diskontinuitas dalam adalah yang memungkinkan kontur dan lipatan sugestif. Saya hanya berdalih dengan pendekatan ini adalah ketidakmampuan untuk memberikan kontrol yang lebih baik atas lebar tepi bertinta, meskipun dengan algoritma yang tepat dalam fragmen shader, saya yakin ini akan mungkin.

Jadi pertanyaannya sekarang menjadi lebih spesifik: Bagaimana tepatnya saya pergi tentang mendapatkan lebar tepi variabel, terutama pada siluet luar, dalam shader fragmen?

Insinyur
sumber
Memvariasikan ketebalan garis adalah fitur pendekatan geometris. Tidak bisa mendapatkannya secara efisien dengan cara lain. Karena Anda menginginkannya, saya tidak melihat pixel shader sebagai "metode yang lebih efisien": jumlah piksel yang akan dicari = (line_thickness * 2 + 1) ^ 2 - 1. Itu pada dasarnya berarti bahwa shader post-proses Anda akan jauh lebih lambat (perkiraan: 10x) jika maks. lebar garis sama dengan 2. Cukup jatuhkan pra-penjurian dan coba pendekatan geometri shader.
snake5
@ snake5 Anda dengan jujur ​​menyarankan agar saya menjalankan 150 juta simpul per pembaruan render (sekitar 25 juta dengan view culling), di GS? Saya tidak benar-benar membeli itu, tapi terima kasih atas masukannya. Lihat kembali apa yang saya nyatakan di atas tentang jumlah dan kompleksitas simpul. Bahkan dengan angka 10x Anda, shader fragmen setidaknya akan menarik bahkan, dan mungkin melakukan jauh lebih baik.
Insinyur
25 juta simpul (linear baca / tulis) vs. 23 juta sampel tekstur (memori cache agak buruk dibaca + dekompresi data target render) @ 1280x720. Membatasi / mengurangi jumlah titik jauh lebih mudah, omong-omong. Terutama hari ini, ketika monitor besar (1920x1080 +) & pengaturan multimonitor menjadi sangat populer.
snake5
bagaimana dengan menggunakan algoritma yang sama dengan yang menghasilkan garis merah, dan kemudian menipis / menumbuhkan garis berdasarkan kedalamannya?
Ali1S232
@ Gajoo, Seperti yang saya katakan dalam pertanyaan, ini bukan hanya tentang siluet. Perhatikan garis-garis mengganggu di belakang bahu gadis itu di gambar pertama. Itu adalah fungsi dari pendekatan flipped-normals, dan bertindak sebagai kontur / lipatan sugestif. Saya perlu yang sama.
Insinyur

Jawaban:

4

Saya telah memutuskan untuk menggunakan pendekatan fragmen shader melalui pemfilteran diskontinuitas buffer kedalaman. Alasan untuk ini adalah:

  1. World vertex count sangat, sangat tinggi karena jarak pandang yang sangat besar, bahkan dengan mesh LoD;
  2. Saya sedang melakukan sejumlah operasi shader fragmen lainnya, seperti DoF blur yang dapat mengambil manfaat dari struktur yang sama (kotak atau gaussian sampling / filtering) dalam lintasan yang sama.

Setelah mengujinya, saya akan mengatakan bahwa dalam proyek mendatang saya akan menggunakan pendekatan berbasis geometri, karena alasan kompleksitas. Alasannya adalah bahwa (seperti yang disarankan dalam komentar), pendekatan fragmen shader untuk deteksi tepi dapat menjadi sangat intensif secara komputasi, khususnya dengan implementasi DoF di mana lingkaran jari-jari kebingungan, dan dengan demikian jumlah sampel per fragmen, bisa sangat tinggi. Untungnya hal ini kurang menjadi perhatian bagi penggaris garis besar.

Insinyur
sumber
Saya pikir pergi dengan diskontinuitas normals akan memberi Anda hasil yang lebih baik jika Anda mendapat normals datar. Anda dapat memvariasikan lebar garis dengan mengambil sampel lebih jauh.
Grieverheart
@ Grieverheart Ini adalah jawaban yang diterima, karena metode yang saya gunakan berfungsi dengan baik. Terima kasih.
Insinyur
0

Sebenarnya ada solusi umum yang sangat mudah jika Anda dapat menggunakan efek setelah. Yang hebat adalah, tidak masalah seberapa tinggi poli-count Anda. Jadikan peta Kedalaman sebagai skala abu-abu, lalu buat sebuah titik dalam warna garis yang Anda inginkan, ketika kontras antara dua piksel yang berdekatan lebih tinggi dari nilai ambang. Anda dapat membuat titik lebih besar sesuai dengan kontras dan atau tingkat cahaya dari gambar yang Anda buat.

Saya menemukan algoritma toonShader kembali pada tahun 2000/2001, bahkan sebelum pria Prancis itu, yang menciptakan solusi ini. Tambang didasarkan pada geometri bahan aktual. Kemudian pada dasarnya ada dua cara untuk melakukannya: 1. lihat normals, jika normals dua pesawat yang terhubung ke garis menghadap ke arah dan ke arah kamera, render garis itu, Anda kemudian dapat menggunakan kedalaman, pencahayaan dll sebagai isyarat untuk linedepths. 2. Lihatlah geomerty yang diberikan (jadi setelah transformasi perspektif) Ambil setiap garisegegmen, jika simpul dari bidang yang menyambung berada di sisi yang sama dari garisegegmen ini, Anda membuat garis. Anda kemudian dapat melakukan hal yang sama untuk ketebalan garis seperti pada metode 1. 3. Anda bisa membuat kombinasi dari ketiga teknik ini, tetapi saya menyebutkan yang pertama, karena Anda menunjukkan jumlah-poli yang sangat besar.

SnoepGames
sumber