OpenGL - Deteksi tepi

12

Saya ingin memuat jerat sewenang-wenang dan menggambar garis hitam tebal di sepanjang tepi untuk mendapatkan tampilan seperti toon-shading. Saya berhasil menggambar siluet hitam di sekitar objek dengan menggunakan buffer stensil. Anda dapat melihat hasilnya di sini:

masukkan deskripsi gambar di sini

Namun yang hilang adalah garis-garis hitam pada objek itu sendiri. Saya berpikir untuk memeriksa diskontinuitas normal: Memeriksa apakah piksel tetangga memiliki vektor normal yang berbeda dari yang ada sekarang. Jika ya, tepi telah ditemukan. Sayangnya, saya tidak tahu bagaimana saya bisa menerapkan pendekatan ini, baik di OpenGL maupun di GLSL vertex / fragment shader.

Saya akan sangat senang atas bantuan tentang pendekatan ini atau lainnya tentang deteksi tepi.

Sunting: Saya tidak menggunakan tekstur apa pun untuk jerat saya.

Terlalu lebih tepat, saya ingin membuat solusi CAD / CAM yang terlihat sebanyak mungkin seperti ini (diambil dari Top Solid https://www.youtube.com/watch?v=-qTJZtYUDB4 ):

masukkan deskripsi gambar di sini

enne87
sumber
Saya percaya Anda perlu mendefinisikan "edge" lebih detail. Apa bedanya dengan wireframe sederhana? Dalam jawaban cifz ada deskripsi yang baik dari ruang layar pasca-proses, tetapi dari pertanyaan Anda sulit untuk menentukan apakah itu berlaku.
Andreas
Nah, dengan edge yang saya maksud adalah "lipatan" dan "ridges" yang membentuk padatan. Bingkai gambar akan menampilkan semua wajah segitiga, yang bukan yang saya inginkan.
enne87
Ok, persis apa yang saya minta :-) Saya akan menghasilkan bingkai gambar dari lipatan dan punggung bukit itu. Bagian yang sulit masih untuk menentukan apa lipatan / punggung bukit. Apakah Anda tahu cara melakukannya?
Andreas
1
Sebagian besar program cad tidak melakukan ini dengan shader. Alih-alih mereka tahu tepi keras dari model dan menggambar garis di atas jala bentuk info itu.
joojaa
joojaa apakah Anda memiliki informasi lebih lanjut tentang teknik ini? Apa yang dilakukan dalam kasus permukaan bentuk melengkung ganda? Saya juga tidak yakin apa yang terjadi ketika Anda memiliki kerucut atau silinder yang sedang dipotong / dipangkas oleh bentuk bebas. stackoverflow.com/questions/43795262/...
Dusan Bosnjak 'pailhead'

Jawaban:

18

Deteksi tepi biasanya dilakukan untuk mendeteksi area gambar dengan nilai gradien tinggi.

Dalam kasus kami, kami dapat dengan kasar melihat gradien sebagai turunan dari fungsi gambar, oleh karena itu besarnya gradien memberi Anda informasi tentang seberapa banyak gambar Anda berubah secara lokal (dalam hal piksel / texels tetangga).
Sekarang, edge adalah seperti yang Anda katakan indikasi diskontinuitas, jadi sekarang kita mendefinisikan gradien jelas bahwa info inilah yang kita butuhkan. Setelah kami menemukan gradien gambar, itu hanya masalah menerapkan ambang batas untuk mendapatkan tepi nilai biner / non-tepi.

Bagaimana Anda menemukan gradien ini benar-benar apa yang Anda minta dan saya belum menjawab :)

Banyak cara! Di sini pasangan :)

Fungsi shader bawaan

Baik hlsl dan glsl menawarkan fungsi turunan. Dalam GLSL Anda memiliki dFdx dan dFdy yang memberi Anda masing-masing informasi gradien dalam arah x dan y. Biasanya fungsi-fungsi ini dievaluasi dalam blok fragmen 2x2.
Kecuali jika Anda tertarik pada satu arah, cara yang baik untuk memiliki hasil yang ringkas yang menunjukkan seberapa kuat gradien di wilayah adalah fwidth yang tidak memberi Anda apa pun selain jumlah nilai absolut dari dFdy dan dFdy.
Anda mungkin tertarik pada keunggulan pada keseluruhan gambar daripada pada saluran tertentu, jadi Anda mungkin ingin mengubah fungsi gambar Anda menjadi luma. Dengan mengingat hal ini, ketika datang ke deteksi tepi shader Anda dapat mencakup sesuatu seperti:

  float luminance = dot(yourFinalColour,vec3(0.2126, 0.7152, 0.0722));
  float gradient = fwidth(luminance );
  float isEdge = gradient > threshold;

Dengan ambang tinggi Anda akan menemukan tepi kasar dan Anda mungkin kehilangan beberapa, sebaliknya, dengan ambang rendah Anda mungkin mendeteksi tepi palsu. Anda harus bereksperimen untuk menemukan ambang yang lebih sesuai dengan kebutuhan Anda.

Alasan mengapa fungsi-fungsi ini layak disebutkan tetapi saya tidak punya waktu untuk itu sekarang, saya kemungkinan akan memperbarui jawaban ini nanti :)

Ruang layar pasca proses

Anda bisa menjadi lebih hebat dari ini, sekarang bidang deteksi Edge dalam pemrosesan gambar sangat besar. Saya bisa mengutip Anda puluhan cara bagus untuk mendeteksi deteksi tepi sesuai dengan kebutuhan Anda, tetapi mari kita tetap sederhana untuk saat ini, jika Anda tertarik, saya dapat mengutip Anda lebih banyak pilihan!

Jadi idenya akan mirip dengan yang di atas, dengan perbedaan bahwa Anda bisa melihat lingkungan yang lebih luas dan menggunakan satu set bobot pada sampel sekitarnya jika Anda mau. Biasanya, Anda menjalankan konvolusi pada gambar Anda dengan kernel yang memberi Anda info gradien yang baik.
Pilihan yang sangat umum adalah kernel Sobel

                                   masukkan deskripsi gambar di sini

Yang masing-masing memberi Anda gradien dalam arah x dan y:

                                  Dari pdf lama saya menulis sejak lama.

GradientMagnitude=(Gradientx)2+(Gradienty)2

Maka Anda bisa mengirik seperti cara yang saya sebutkan di atas.

Kernel ini seperti yang Anda lihat memberi bobot lebih pada pixel pusat, jadi secara efektif menghitung gradien + sedikit smoothing yang secara tradisional membantu (seringkali gambar buram untuk menghilangkan tepi kecil).

Cara di atas berfungsi dengan baik, tetapi jika Anda tidak suka smoothing, Anda bisa menggunakan kernel Prewitt:

                                                   masukkan deskripsi gambar di sini

(Catatan saya sedang terburu-buru, akan segera menulis teks yang diformat alih-alih gambar!)

Benar-benar ada lebih banyak kernel dan teknik untuk menemukan deteksi tepi dalam proses gambar-y daripada grafis waktu nyata, jadi saya telah mengecualikan metode yang lebih berbelit-belit (pun tidak dimaksudkan) karena mungkin Anda akan baik-baik saja dengan fungsi dFdx / y .

cifz
sumber
Cifz penjelasan yang sangat bagus, tetapi bagaimana jika tidak ada gradien terlihat dalam situasi tertentu? Misalnya, tidak ada sumber cahaya di bagian belakang kubus dan karenanya, tidak ada gradien yang terlihat. Maka proses deteksi tepi berbasis gambar yang Anda gambarkan ini tidak akan berhasil, apakah saya benar?
enne87
Jika Anda menggunakan penyaji ditangguhkan Anda dapat melakukan hal yang sama tetapi pada buffer normal atau lagi, jika Anda memiliki prepass Anda dapat menerapkan algoritma ke kedalaman. Masih Anda benar, pendekatan ruang layar mungkin tidak ideal dalam semua kasus :) Solusi apa yang layak sangat tergantung pada berapa anggaran Anda untuk efek ini, seberapa kompleks adegan Anda.
cifz
Berpotensi jika ini benar-benar penting untuk gim Anda, yang sangat mudah, tetapi berpotensi melelahkan, akan menghitung gradien pada normals tetangga Anda untuk setiap dhuwur (offline saat memuat) dan meneruskan faktor ini sebagai atribut simpul tambahan.
cifz
Terima kasih lagi cifz atas bantuan Anda. Nah, apa yang ingin saya capai adalah mengembangkan solusi CAD / CAM yang mirip dengan ini: youtube.com/watch?v=-qTJZtYUDB4 Dan saya benar-benar ingin tahu bagaimana mereka mengaturnya untuk membuat semua tepi hitam, lipatan dan punggung bukit. cifz, apakah Anda berpikir sebagai spesialis pemrograman grafis bahwa mereka mencapai tampilan ini dengan menggunakan salah satu pendekatan ruang layar Anda? Atau mungkin dengan menghitung gradien pada normals tetangga? Saya benar-benar ingin tahu bagaimana ini bisa dilakukan. Terima kasih lagi!
enne87
1
Tidak perlu membayar siapa pun, mulailah membaca beberapa tutorial online, beli beberapa buku, dan belajar keras :) Ini akan sangat bermanfaat di akhir!
cifz
3

Untuk berjaga-jaga jika ada yang juga perlu mendeteksi tepi: Berikut ini adalah artikel yang bagus tentang cara menampilkan bingkai gambar dan artikel ini menjelaskan cara hanya menampilkan tepi.

enne87
sumber