Bagaimana cara saya menulis shader yang menyala ketika benda berada di dekat permukaan?

15

Dalam video gameplay Overwatch ini , perisai karakter menyala putih di area yang dekat geometri objek lain.

Perisai Reinhart memotong beberapa tingkat geometri

Perhatikan tepi putih pada perisai biru, dekat lantai, dinding, dan pilar.

Saya percaya perisai memiliki modelnya sendiri dan pengaruhnya dilakukan dengan shader, tetapi saya bingung mencoba untuk menerjemahkan konsep "kedekatan" ke pemrograman shader.

Bug Biru
sumber
2
Jika Anda menggunakan Unity, pembicaraan ini mungkin menarik . Periksa bagian "Sorotan titik-temu" mulai dari slide 26.
DMGregory
Jadi jawabannya sudah dibahas di bawah ini, tetapi inilah video yang sekarang menjelaskannya: youtube.com/watch?v=C6lGEgcHbWc
CobaltHex

Jawaban:

28

Garis besar umum:

  1. Buat peta kedalaman adegan Anda tanpa perisai. Anda bisa mendapatkan ini secara efektif secara gratis, karena objek transparan sering kali diberikan dalam pass kemudian. Jika tidak, Anda dapat membuat peta kedalaman dengan merender adegan tanpa pelindung ke RTT dengan shader kedalaman.

  2. Jadikan adegan Anda secara normal, berikan peta kedalaman ke shader pelindung Anda.

  3. Di shader, hitung perbedaan kedalaman adegan dari kedalaman fragmen perisai, dan gunakan perbedaan itu untuk memodifikasi warna fragmen.

Demo

Saya menulis demo WebGL sederhana tentang itu.

tangkapan layar demo

Baris demi baris

Mari kita membahas kode shader fragmen secara terperinci:

float solidsDepth = texture2D(depthMap, gl_FragCoord.xy / dims).r;

Sampel peta kedalaman di setiap fragmen. Ingatlah untuk membaginya dengan dimensi viewport Anda untuk mengonversi fragmen Anda dari ruang layar [0, lebar / tinggi] ke koordinat [0,0, 1.0] yang dinormalisasi. Pada titik ini, jika Anda cukup mengatur warna fragmen ke piksel peta kedalaman sampel, akan terlihat seperti ini:

tangkapan layar peta kedalaman

Peta kedalaman berwarna abu-abu, sehingga Anda bisa mendapatkan nilai dari saluran apa pun (saya gunakan di rsini).

float solidsDiff = 1.0 - smoothstep(
    zNear,
    zFar,
    gl_FragCoord.z / gl_FragCoord.w
  ) - solidsDepth;

Anda kemudian dapat menggunakan sampel kedalaman untuk menemukan perbedaan antara kedalaman adegan dan kedalaman fragmen perisai. Ingatlah untuk menormalkan kedalaman Anda juga, untuk mengambilnya dari [zNear, zFar] (pesawat dekat dan jauh dari kamera Anda) ke [0,0, 1.0]. smoothstepmelakukan ini dengan baik. The 1.0 -adalah untuk membalikkan nilai seperti yangsolidsDiff adalah 1,0 ketika perbedaannya adalah maksimum (zFar - zNear) dan 0,0 minimal (0,0).

Perhatikan bahwa saya berasumsi solidsDepthsudah dinormalisasi di shader kedalaman yang membuat peta kedalaman.

float alpha = 0.3 + max(0.0, 1.0 - log(100.0 * (solidsDiff - 0.005) + 1.0));

Anda kemudian dapat memodifikasi saluran alfa perisai Anda tergantung pada perbedaan kedalaman. Di sini kita mulai dari alfa minimum 0.3, lalu buat peningkatan alfa yang tajam saat kita mendekati 0.0perbedaan.

The - 0.005diimbangi hanya menambahkan margin putih untuk membuat "persimpangan" lebih tebal. Coba modifikasi!

gl_FragColor = vec4(vec3(1.0), alpha);

Dan akhirnya, terapkan alpha itu ke warna fragmen Anda.


Perangkat tambahan

Anda bisa membuat perisai melengkung, menambahkan plasma untuk tampilan "perisai energi" (demo) atau menjelajahi efek hanya dengan menunjukkan persimpangan (demo) .

Langit Kartu grafis Anda adalah batasnya!

Yousef Amar
sumber
Oh Saya. Kabar baik. Ty untuk tutorial yang luar biasa.
Blue Bug
5

Itu hanya menggunakan peta kedalaman. Ini membuat dunia kemudian membuat perisai dan mengambil perbedaan antara nilai z yang diberikan perisai dan nilai buffer kedalaman z untuk mewarnai piksel lebih putih.

Sirisian
sumber
3

Saya tidak tahu Mesin mana, jika ada, yang Anda gunakan. Atau bahasa apa yang Anda gunakan. Namun, sebagian besar dari apa yang dapat Anda temukan online tidak sulit untuk berpindah dari satu lingkungan ke lingkungan lain untuk mencapai apa yang Anda cari.

Dan tentu saja ada materi online yang dapat membantu Anda. Lihat diskusi ini terkait dengan Unity: http://www.superspacetrooper.com/2012/06/tutorial-force-field-weapon-impact-energy-dispersion/ dan pertanyaan ini terkait UE4: https: //answers.unrealengine. com / pertanyaan / 74858 / dynamic-forcefield-shader.html . Untuk contoh shader lengkap, menerapkan efek perisai, dari debat baru-baru ini di Reddit, Anda dapat melihat: https://www.reddit.com/r/Unity3D/comments/3edi0n/does_any_one_knows_how_to_make_this_shield_effect/ Dan untuk tutorial yang tidak persis pada itu tetapi cukup terkait dengan minat: http://www.nightbox-studios.com/2015/09/09/assets-shield-effect-scripts-for-texture3d-perlin-noise-shader/

Juga, berikut adalah tautan untuk 3 pertanyaan terkait yang dibuat sebelumnya di situs ini, yang mungkin akan sangat membantu untuk memahami konsep di balik apa yang ingin Anda capai:

Flare Shield Spaceship

Cara menerapkan perisai energi starwar dalam game

Efek perisai XNA dengan masalah bola Primatif

Terakhir, ada beberapa implementasi shader pelindung yang bagus di Unity dan di Unreal Engine di toko virtual masing-masing, jika Anda menggunakan salah satu dari mesin tersebut. Mereka umumnya dibayar aset tentu saja, tetapi hampir selalu open source setelah membeli - dan seringkali murah. Sekalipun Anda tidak kebetulan menggunakan mesin ini, aset itu dapat membantu bermain-main dan belajar.

Semoga ini bisa membantu.

Dan
sumber
Meskipun tautan tersebut sangat membantu, jawaban ini pada dasarnya hanyalah tautan dan tidak benar-benar menjawab pertanyaan secara langsung. Akan lebih baik untuk mengekstraksi konten tautan yang relevan ke dalam penjelasan yang sebenarnya.
Anko
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban hanya tautan dapat menjadi tidak valid jika halaman tertaut berubah. - Dari Ulasan
Vaillancourt
@Anko Tentu, salahku. Saya biasanya menyertakan penjelasan dan kumpulan tautan, tetapi kali ini Anda benar, penjelasan sebenarnya tidak ada. Saya mungkin akan menghapus jawabannya, lalu.
Juli
@AlexandreVaillancourt Saya tidak suka gagasan hanya salin-tempel dari tautan lain alih-alih mengarahkan OP ke sumber aslinya. Lalu, yang saya pikir terbaik adalah memberikan tautan di komentar ke pertanyaan. Masalahnya adalah ketika Anda tahu banyak materi yang bisa membantu tetapi terlalu besar untuk komentar. Tapi tetap, karena jawaban yang baik penjelasan sudah diposting di sini, saya hanya akan menghapus satu ini
Mand