Alfa tertunda berbaur

Pada artikel ini saya ingin berbicara tentang metode untuk mencampur geometri raster. Model pencampuran klasik dari objek transparan - Alpha, Additive, Multiplicative - disatukan oleh prinsip gambar yang sama: menggambar secara berurutan satu primitif demi satu, mencampur piksel yang diterima pada output shader fragmen dengan apa yang ada di buffer saat ini. Setiap primitif baru memperbarui area buffer ke mana ia ditarik; dalam kasus alpha blending, objek-objek yang lebih tinggi yang sebelumnya ditarik sebelumnya. Tetapi bagaimana jika Anda ingin melakukan sesuatu dengan sekelompok objek yang digambar di atas layar - misalnya, memotongnya dengan topeng atau menyorotinya? Di sini dua keputusan segera terlintas dalam pikiran: apakah membuat perubahan pada materi mereka (mis. Mengubah shader, memperluas rangkaian tekstur), misalnya, menambahkan proyeksi tekstur lain,yang akan bertanggung jawab atas topeng transparansi. Namun, jika kita memiliki banyak objek berbintik, mengubah setiap materi unik tidak nyaman dan penuh dengan kesalahan. Opsi kedua adalah menarik semua objek yang menarik kepada kami menjadi target layar penuh yang terpisah dan menggambarnya sudah di adegan terakhir. Di sini kita dapat melakukan apa pun yang kita inginkan dengan isinya, tetapi ini membutuhkan alokasi memori tambahan dan, yang paling tidak menyenangkan, mengganti rendering target. Ini bukan operasi "termurah" pada perangkat seluler, yang perlu dilakukan dua kali. Dan jika Anda ingin bekerja dengan beberapa lapisan seperti ini?Di sini kita dapat melakukan apa pun yang kita inginkan dengan isinya, tetapi ini membutuhkan alokasi memori tambahan dan, yang paling tidak menyenangkan, mengganti rendering target. Ini bukan operasi "termurah" pada perangkat seluler, yang perlu dilakukan dua kali. Dan jika Anda ingin bekerja dengan beberapa lapisan seperti ini?Di sini kita dapat melakukan apa pun yang kita inginkan dengan isinya, tetapi ini membutuhkan alokasi memori tambahan dan, yang paling tidak menyenangkan, mengganti rendering target. Ini bukan operasi "termurah" pada perangkat seluler, yang perlu dilakukan dua kali. Dan jika Anda ingin bekerja dengan beberapa lapisan seperti ini?



Ada cara lain, lebih sederhana dan lebih elegan untuk menyelesaikan masalah ini. Kita bisa melukis pemandangan secara terbalik!

Penyimpangan kecil untuk mengingatkan Anda bagaimana metode rendering klasik bekerja
- Alpha Blending , , , . 4 RGBA, RGB โ€” A(Alpha) โ€” ( ). . :


ColorSrc โ€” RGB , ( , ), ColorDst โ€” , , Color_Result โ€” , , , . Variable1 Variable2, ? , ( , ). , : , ... 

:


AlphaSrc โ€” - (), OneMinusAlphaSrc, , 1.0 โ€” AlphaSrc. : 1 * + 2 * (1 โ€” ). alpha (). = 1, , = 0, . OpenGL .

OpenGL ES 2.0 โ€” .

Bagaimana gambar terbentuk dalam langkah-langkah: pertama kita menggambar latar belakang, lalu kita menggambar semua objek berlapis-lapis. Apa yang diberikan terakhir menimpa piksel sebelumnya: 





Apa masalahnya? 


Inti dari teknologi rendering terbalik atau, sebagaimana dapat juga disebut, pencampuran tertunda adalah sebagai berikut. Kami menggambar adegan mundur menggunakan formula campuran yang berbeda. Selain itu, gambar akhir akan tetap sama persis dengan pendekatan klasik.

Bagaimana itu bekerja?


Metode pencampuran melalui saluran transparansi gambar yang kami gambar dijelaskan di atas. Sekarang kita akan membalikkannya: kita akan menggunakan transparansi piksel yang sudah ditarik (atau lebih tepatnya, mencampur transparansi yang ditarik dengan yang sudah ditarik). Artinya, alih-alih AlphaSrc kita akan menggunakan AlphaSaturate, dan bukannya OneMinusAlphaSrc - One. Ternyata jika sudah ada sesuatu dengan transparansi = 1 di buffer, maka kontribusinya akan menjadi nol, dan warna piksel tersebut tidak akan berubah. Jika tidak ada transparansi, mari kita tambahkan kedua warna bersama-sama (untuk ini kita perlu menghapus buffer bingkai dengan nol atau hitam dengan transparansi nol). Dengan penambahan ini, warna yang dihasilkan akan sama dengan yang ditarik. Rumus akhir terlihat seperti ini:
 

(sekitar. AlphaSaturate = min (AlphaSrc, 1 - AlphaDst))

Nilai transparansi perlu ditambahkan: ia harus mengakumulasi lapisan demi lapisan, yaitu, kita akan memiliki Satu dan Satu dalam variabel campuran untuk saluran alpha. Mengapa kita tidak memodifikasi ColorDst dan menghapus buffer dengan nol? Ini diperlukan untuk pencampuran Aditif, AdditiveBlending hanya akan berbeda karena akan memiliki Nol dalam variabel AlphaSrc. Seharusnya tidak mengubah transparansi, hanya warna.

Untuk kejelasan, skema render terbalik terlihat seperti ini: 

Pertama, kami menghapus buffer bingkai. Kemudian kita mengatur fungsi pencampuran yang diberikan di atas dan mulai menggambar dari objek paling atas (dalam pendekatan klasik, mereka akan ditarik terakhir), turun ke yang bawah. Gambar latar belakang akan diambil terakhir.


Bagaimana ini bisa digunakan?


Saya akan menjelaskan beberapa tugas yang diselesaikan dengan metode ini, menggunakan proyek kami sebagai contoh:

  1. Memotong objek dengan topeng dengan transparansi. Kliping halus ruang permainan:


    Setelah menggambar lapangan bermain, cukup untuk menghapus transparansi di tempat-tempat gambar yang ingin kita sembunyikan. Hal ini dilakukan dengan menggunakan formula campuran, di mana objek topeng yang ditarik menimpa warna dan transparansi berbanding terbalik dengan transparansi sendiri, dan tingkat pemurnian dapat terus disesuaikan. Dalam hal ini, geometri berikut digunakan untuk memotong:


    Ini berubah bentuk saat kamera bergerak di antara kamar. Formula pencampuran untuk pembersihan adalah sebagai berikut:

    ColorSrc = GL_ZERO,
    ColorDst = GL_ONE_MINUS_SRC_ALPHA,
    AlphaSrc = GL_ZERO,
    AlphaDst = GL_ONE_MINUS_SRC_ALPHA

    Anda dapat menggunakan geometri apa saja dengan tekstur apa pun, mulai membersihkan dari lapisan yang Anda perlukan:

  2. Hilangnya bidang secara halus juga dilakukan dengan cara yang sama. Harga masalah adalah satu DrawCall.
  3. :


    , UI, . , ยซยป , , ยซ ยป, . , : , . :

    ColorSrc = GL_SRC_ALPHA,
    ColorDst = GL_ONE_MINUS_SRC_ALPHA,
    AlphaSrc = GL_ZERO,
    AlphaDst = GL_ONE
  4. , :


  5. ยซยป:



    . , . โ€” 2 DrawCalls.



Ambient occlusion





Ada kelemahan, atau lebih tepatnya pembatasan: tidak semua campuran dapat diulang untuk teknik seperti itu. Pencampuran alfa dan aditif pasti dimungkinkan, tetapi Anda harus beradaptasi atau tidak menggunakan campuran khusus Anda sendiri. Tapi ada jalan keluar: Anda bisa memisahkan tahapan rendering adegan. Sebagian darinya harus dilakukan dengan metode terbalik, sebagian lagi biasa, dan inilah yang kami lakukan untuk efek khusus di atas lapangan dan pasca-proses.

Poin penting dengan teknik rendering Aditif dan campuran: jika digambar SEBELUM bagian rendering terbalik, dan jika tidak ada informasi transparansi dalam tekstur (tekstur seperti "bintik putih pada latar belakang hitam"), maka objek tersebut akan menimpa transparansi. Dalam bagian "kembali", informasi tentang bagian ini akan hilang, dan secara visual akan terlihat seperti "kotak gelap" atau perbatasan hitam di sekitar titik aditif cahaya:


Ini dapat diatasi dengan memodifikasi campuran aditif dalam hal mencampur saluran alpha:

AlphaSrc = GL_ONE_MINUS_DST_ALPHA
AlphaDst = GL_ONE

Tetapi ini tidak cocok untuk semua jenis pencampuran, dan akan lebih andal untuk memodifikasi tekstur itu sendiri. Apa yang dimaksud:

Jika ada tekstur bentuk: 


Maka Anda perlu melakukan salah satunya:


Artinya, kecerahan saluran warna harus dikonversi menjadi transparansi dan meregangkan warna berbanding terbalik dengan transparansi. Tekstur yang dihasilkan dan yang lama harus terlihat sama pada latar belakang hitam. Secara manual ini tidak mungkin berhasil, masuk akal untuk membuat konverter otomatis. Dalam hal ini, kode semu konversi saluran akan terlihat seperti ini:

RGB_old = Texel_in.rgb
A_old = Texel_in.a
A_middle = 1.0 / ((RGB_old) / 3.0) * A_old // linear color space
RGB_new = RGB_old * A_middle;
A_shift = minimum( 1.0 / RGB_new.r, 1.0)
A_shift = minimum( 1.0 / RGB_new.g, A_shift)
A_shift = minimum( 1.0 / RGB_new.b, A_shift)
RGB_result = RGB_new * A_shift; 
A_result = (RGB_result) / 3.0)
Texel_out = Vector4(RGB_result, A_result)



Di sini saya akan melalui langkah-langkah rendering adegan proyek kami
 
  1. . , -.

  2. , , , ยซยป :


  3. UI:

  4. , , , :

  5. :

  6. :

  7. , .




Kesimpulan


Metode ini memungkinkan untuk bekerja cukup sederhana dan murah dengan lapisan adegan yang ditarik, menggunakan saluran alpha sebagai topeng. Ini relatif sederhana untuk mengimplementasikannya dalam proyek yang sudah berjalan: tidak memerlukan modifikasi mendalam dari kode subsistem grafis, cukup untuk mengubah urutan rendering dan formula pencampuran. Di beberapa tempat, ini dapat secara signifikan menghemat kinerja. Ada beberapa batasan, tetapi dalam banyak kasus Anda bisa menerima kenyataan itu.

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


All Articles