Cara kerja penggabungan alfa

gambar

Transparansi mungkin tidak tampak seperti topik yang menarik. Format GIF, yang memungkinkan beberapa piksel bersinar melalui latar belakang, diterbitkan lebih dari 30 tahun yang lalu. Hampir setiap aplikasi desain grafis yang dirilis selama dua dekade terakhir mendukung pembuatan konten yang tembus cahaya. Konsep-konsep ini telah lama berhenti menjadi sesuatu yang baru.

Dalam artikel saya, saya ingin menunjukkan bahwa sebenarnya transparansi dalam gambar digital jauh lebih menarik daripada yang terlihat - dalam apa yang kita anggap remeh, ada kedalaman dan keindahan yang tidak terlihat.

Opacity


Jika Anda pernah melihat melalui kacamata merah muda, maka Anda bisa melihat sesuatu yang mirip dengan apa yang ditunjukkan pada gambar di bawah ini. [Dalam artikel asli, banyak gambar bersifat interaktif.] Coba gerakkan kacamata untuk melihat bagaimana mereka memengaruhi apa yang terlihat melalui mereka:


Kacamata seperti ini berfungsi sebagai berikut: kacamata kehilangan warna merah, warna biru yang layak dan sedikit sekali warna hijau. Matematika poin-poin ini dapat ditulis dalam satu set tiga persamaan. Huruf R menunjukkan hasil operasi, dan huruf D menjelaskan poin yang sedang kita lihat. Indeks RGB menunjukkan komponen merah, hijau, dan biru:

R R = D R × 1.0
R G = D G × 0.7
R B = D B × 0.9

Kaca patri ini mentransmisikan komponen latar belakang merah, hijau, dan biru dengan berbagai kekuatan. Dengan kata lain, transparansi kacamata merah muda tergantung pada warna cahaya yang terjadi. Secara umum, transparansi dapat bervariasi tergantung pada panjang gelombang cahaya , tetapi dalam contoh sederhana ini, kami hanya tertarik pada bagaimana kacamata mempengaruhi komponen RGB klasik.

Mensimulasikan perilaku kacamata hitam biasa jauh lebih sederhana, mereka biasanya hanya menipiskan cahaya kejadian dengan sejumlah:


Kacamata ini memungkinkan hanya 30% dari cahaya yang melewatinya. Perilaku mereka dapat dijelaskan oleh persamaan berikut:

R R = D R × 0.3
R G = D G × 0.3
R B = D B × 0.3

Ketiga komponen warna dikurangi dengan nilai yang sama - penyerapan cahaya insiden adalah sama. Kita dapat mengatakan bahwa kacamata gelap 30% transparan (buram) atau 70% buram. Opacity suatu objek menentukan berapa banyak warna yang diblok. Dalam grafik komputer, kami biasanya berurusan dengan model yang disederhanakan di mana hanya satu nilai yang diperlukan untuk menggambarkan properti ini. Opacity dapat bervariasi secara spasial. seperti, misalnya, kolom asap yang menjadi lebih tinggi dan lebih transparan.

Di dunia nyata, objek dengan opacity 100% hanyalah buram dan mereka tidak mentransmisikan cahaya sama sekali. Dunia gambar digital sedikit berbeda. Ada kasus batas di dalamnya ketika bahkan benda buram padat melewati sejumlah cahaya.

Cakupan


Grafik vektor berhubungan dengan deskripsi bentuk yang jelas dan akurat tanpa batas yang didefinisikan menggunakan titik, segmen garis, kurva Bezier, dan matematika primitif lainnya. Saat Anda perlu menampilkan gambar di layar komputer, entitas yang sempurna ini harus dirasterisasi menjadi bitmap:


Rasterisasi bentuk vektor ke bitmap

Cara yang paling primitif untuk merasterisasi adalah memeriksa di mana sampel piksel berada - di dalam atau di luar bentuk vektor. Pada contoh di bawah ini, Anda dapat menarik segitiga, dalam tampilan yang diperbesar, gerakannya akan lebih akurat. Garis biru menunjukkan geometri vektor asli. Seperti yang Anda lihat, tangga di tepi segitiga terlihat jelek dan sering berkedip ketika memindahkan geometri:


Kerugian dari pendekatan ini adalah bahwa kami hanya melakukan satu pemeriksaan untuk setiap piksel yang ditampilkan, dan hasilnya didiskritkan ke salah satu dari dua nilai yang mungkin - di dalam atau di luar.

Anda dapat mencicipi geometri vektor beberapa kali per piksel untuk mendapatkan gradasi langkah besar dan memutuskan bahwa beberapa piksel hanya ditutup sebagian . Salah satu solusi yang mungkin adalah dengan menggunakan empat titik pengambilan sampel untuk mewakili lima tingkat cakupan: 0, 14 , 24 , 34, dan 1:


Kualitas tepi segitiga telah meningkat, tetapi hanya lima tingkat cakupan yang mungkin sering tidak cukup dan kita dapat dengan mudah mencapai hasil yang lebih baik. Meskipun pandangan piksel sebagai kotak kecil di dunia pemrosesan sinyal dipandang dengan ketidaksetujuan , dalam beberapa konteks itu adalah model yang berguna yang memungkinkan kita untuk menghitung cakupan tepat suatu piksel dengan geometri vektor. Perpotongan garis dan bujur sangkar selalu dapat didekomposisi menjadi trapesium dan persegi panjang :

gambar

Segmen garis membagi persegi menjadi trapesium dan persegi panjang

Anda dapat dengan mudah menghitung luas kedua bagian, dan jumlah mereka dibagi dengan luas persegi menentukan persentase cakupan piksel. Dengan demikian, pertanggungan dihitung sebagai angka pasti dengan akurasi sewenang-wenang. Dalam demo yang ditunjukkan di bawah ini, metode ini digunakan untuk membuat tepian yang jauh lebih baik yang tetap mulus bahkan ketika menyeret segitiga:


Ketika datang ke bentuk yang lebih kompleks, misalnya, elips atau Beziers , mereka sering dibagi menjadi segmen garis lurus sederhana yang memungkinkan Anda untuk menghitung cakupan dengan akurasi yang tepat.

Konsep cakupan parsial sangat penting untuk rendering grafik vektor berkualitas tinggi dan, yang lebih penting, untuk rendering teks. Jika Anda mengambil tangkapan layar dari artikel ini dan mempertimbangkannya dengan saksama, Anda akan melihat bahwa hampir semua tepi piksel glyph hanya mencakup sebagian:


Cakupan sebagian digunakan secara aktif dalam rendering teks

Memiliki opacity objek dan menutupinya dengan piksel individual, Anda dapat menggabungkannya menjadi satu nilai.

Alfa


Produk dari opacity dari suatu objek dan cakupan pixel-nya disebut alpha :

= ×

Objek dengan 60% opacity, mencakup 30% dari area pixel, memiliki nilai alpha 18% dalam pixel ini. Secara alami, ketika objek transparan atau sepenuhnya tidak menutupi piksel, maka nilai alfa dalam piksel ini adalah 0. Setelah perkalian, perbedaan antara opacity dan coating menghilang, yang dalam arti membenarkan fakta bahwa konsep "alpha" dan "opacity" digunakan sebagai sinonim.

Alpha sering direpresentasikan sebagai saluran keempat dari gambar bitmap. Nilai biasa merah, hijau dan biru dilengkapi dengan nilai alpha, membentuk empat nilai RGBA.

Ketika datang untuk menyimpan nilai alpha dalam memori, ada godaan untuk menggunakan hanya beberapa bit untuk ini. Dalam hal menutupi piksel tepi benda-benda buram, tampaknya 4 atau bahkan 3 bit akan cukup, tergantung pada kerapatan piksel layar:


Namun, opacity juga mempengaruhi nilai alfa, sehingga kedalaman bit yang rendah dapat menjadi bencana dalam beberapa kasus transparansi yang berubah dengan lancar. Gambar di bawah ini menunjukkan gradien dari buram hitam menjadi putih, yang menunjukkan bahwa kedalaman bit rendah menghasilkan variasi warna yang sangat kuat:


Jelas, semakin banyak bit, semakin baik, dan paling sering alpha 8 menggunakan kedalaman 8 bit untuk mencocokkan akurasi komponen warna, itulah sebabnya mengapa banyak buffer RGBA menempati 32 bit per pixel. Perlu juga dicatat bahwa, tidak seperti komponen warna, yang sering dikodekan menggunakan transformasi non-linear, alpha disimpan secara linier - nilai yang dikodekan dari 0,5 sesuai dengan nilai alpha dari 0,5.

Berbicara tentang alfa, kami sepenuhnya mengabaikan semua komponen warna lainnya, tetapi selain memblokir warna latar belakang, piksel itu sendiri dapat menambahkan sedikit warna. Idenya cukup sederhana - objek merah muda yang tembus cahaya menghalangi bagian dari pencahayaan latar belakang yang masuk dan memancarkan atau memantulkan sedikit cahaya merah muda:


Perhatikan bahwa itu tidak berperilaku seperti kaca patri. Kaca hanya menghalangi bagian dari pencahayaan latar belakang dengan kecerahan yang berbeda. Jika Anda melihat benda yang sepenuhnya hitam melalui kaca merah muda, maka kegelapannya akan tetap ada, karena benda hitam itu tidak memancarkan dan tidak memantulkan cahaya apa pun. Namun, objek merah muda yang tembus cahaya menambah cahayanya sendiri. Jika Anda meletakkannya di atas objek hitam, hasilnya akan menjadi merah muda. Analog yang baik dari perilaku ini adalah bahan halus yang menggantung di udara, seperti kabut, asap, kabut, atau bubuk berwarna.

Rendering saluran alpha sedikit lebih sulit - objek transparan sempurna tidak terlihat oleh definisi, jadi untuk membedakan antara objek, kita perlu menggunakan dua trik. Latar belakang kotak-kotak menunjukkan bagian gambar mana yang transparan; Pola ini digunakan dalam banyak aplikasi grafis:


Pola catur menunjukkan potongan transparan.

Empat kotak kecil di bawah gambar memberi tahu kita bahwa kita melihat komponen merah, hijau, biru dan alfa dari gambar. Dalam beberapa kasus, akan berguna untuk melihat langsung nilai kanal alfa, dan cara termudah untuk menampilkannya menggunakan nuansa abu-abu:


Tampilkan nilai RGB dan A pada permukaan yang berbeda

Semakin cerah warna abu-abu, semakin tinggi nilai alfa, yaitu, hitam murni sesuai dengan 0% alfa, dan putih murni menjadi 100% alfa. Kotak kecil menunjukkan bahwa komponen RGB dan A dari gambar dibagi menjadi dua bagian.

Komponen alfa itu sendiri tidak terlalu berguna, tetapi menjadi sangat penting ketika kita berbicara tentang pengomposisian.

Pengomposisian sederhana


Sangat sedikit efek rendering 2D yang dapat diimplementasikan dalam satu operasi, dan untuk membuat hasil yang selesai, kami menggunakan proses penggabungan yang menggabungkan berbagai gambar. Misalnya, tombol "Batal" sederhana dapat dibuat dengan mengomposisikan lima elemen terpisah:


Elemen Pengomposisian untuk Tombol Batal

Pengomposisian sering dilakukan dalam beberapa tahap, di mana masing-masing dua gambar digabungkan. Gambar latar depan yang digunakan dalam pengomposisian biasanya disebut sumber . Gambar latar belakang yang digunakan dalam pengomposisian, di mana sumber dilapiskan, biasanya disebut tujuan .

Kami akan mulai dengan mengomposisikan pada latar belakang buram, karena ini adalah kasus yang sangat umum. Segala sesuatu yang Anda lihat di layar pada akhirnya ditumpangkan dengan mengkomposisikan pada tujuan buram.

Ketika nilai alfa sumber adalah 100%, maka sumber itu buram dan harus sepenuhnya mencakup tujuan. Jika nilai alfa adalah 0%, maka sumber sepenuhnya transparan dan tidak mempengaruhi tujuan dengan cara apa pun. Nilai alfa 25% memungkinkan objek memancarkan 25% cahayanya dan melewatkan 75% cahayanya dari latar belakang, dan seterusnya:


Menyusun sumber ungu dengan nilai alfa yang berbeda untuk tujuan kuning

Anda sudah dapat memahami apa yang akan terjadi - kasus sederhana pengomposisian alfa pada latar belakang buram - itu hanya interpolasi linier antara warna tujuan dan sumber. Pada grafik di bawah ini, slider mengontrol nilai alpha dari sumber, dan grafik merah, hijau, dan biru menampilkan nilai-nilai komponen RGB. Hasil R hanyalah campuran antara sumber S dan tujuan D :


Apa yang terjadi di sini dapat dijelaskan oleh persamaan yang ditunjukkan di bawah ini. Seperti sebelumnya, indeks menunjukkan komponen, yaitu, S A adalah nilai alpha dalam sumber, dan DG adalah nilai hijau di tujuan:

R R = S R × S A + D R × (1 − S A )

R G = S G × S A + D G × (1 − S A )

R B = S B × S A + D B × (1 − S A )

Persamaan untuk komponen merah, hijau, dan biru memiliki penampilan yang sama, jadi Anda cukup menggunakan indeks RGB dan menggabungkannya menjadi satu baris:

R RGB = S RGB × S A + D RGB × (1 − S A )

Selain itu, karena tujuan buram dan sudah memblokir semua cahaya latar belakang, kita tahu bahwa nilai alpha dari hasilnya selalu 1:

R A = 1

Pengomposisian pada latar belakang buram itu sederhana, tetapi kemampuannya cukup terbatas. Dalam banyak kasus, diperlukan solusi yang lebih andal.

Buffer menengah


Gambar di bawah ini menunjukkan proses dua langkah pengomposisian tiga lapisan yang berbeda, berlabel A, B, dan C. Simbol mean akan berarti "ditumpangkan oleh pengomposisian pada":


Hasil pengomposisian dua tahap dari tiga lapisan

Pertama kita overlay B dengan C dengan menyusun, dan kemudian overlay A dengan mereka untuk mendapatkan gambar selesai. Dalam contoh berikut, kami akan melakukan hal-hal sedikit berbeda. Pertama, kita akan menghubungkan dua lapisan teratas dengan mengkomposisikan, dan kemudian menampilkan hasilnya pada tujuan terakhir:


Hasil pengomposisian dua tahap dari tiga lapisan dalam urutan yang berbeda

Anda mungkin bertanya-tanya apakah situasi seperti itu muncul dalam praktik, tetapi sebenarnya itu sangat umum. Banyak operasi pengomposisian non-sepele dan efek rendering, seperti masking dan blur, mengharuskan melewati buffer perantara yang hanya berisi sebagian hasil pengompositan. Konsep ini memiliki nama yang berbeda: melewati layar, lapisan transparansi, atau penyangga samping, tetapi biasanya mereka didasarkan pada ide yang sama.

Yang lebih penting bagi kami adalah bahwa hampir semua gambar dengan transparansi dapat dianggap sebagai hasil parsial dari beberapa rendering, yang nantinya akan ditumpangkan dengan mengkomposisikan pada tujuan terakhir:


Pengomposisian sebagian tombol ke clipboard

Kita perlu memahami bagaimana cara mengganti penggabungan gambar tembus A dan B dengan satu gambar (A (B) yang memiliki warna dan opacity yang sama. Mari kita mulai dengan menghitung nilai alpha buffer akhir.

Menggabungkan nilai alpha


Mungkin tidak jelas bagi Anda cara menggabungkan opacity dari dua objek, tetapi lebih mudah untuk membicarakan tugas ini jika kita berbicara tentang transparansi.

Misalkan sejumlah cahaya melewati objek pertama, dan kemudian melalui objek kedua. Jika transparansi objek pertama adalah 80%, maka akan melewati 80% dari cahaya insiden. Demikian pula, objek kedua dengan transparansi 60% akan memungkinkan 60% dari cahaya melewatinya, yang memberi kita 60% × 80% = 48% dari cahaya asli. Anda dapat bereksperimen dengan transparansi di artikel asli; jangan lupa bahwa slider mengontrol transparansi dan bukan opacity objek di jalur cahaya:


Secara alami, ketika objek pertama atau kedua buram, tidak ada cahaya yang melewati mereka, bahkan yang lain benar-benar transparan.

Jika objek D memiliki transparansi D T , dan objek S memiliki transparansi S T , maka transparansi umum akhir RT dari kedua objek ini sama dengan produk mereka:

R T = D T × S T

Namun, transparansi hanyalah unit minus alpha, jadi substitusi memberi kita yang berikut:

1 - R A = (1 - D A ) × (1 - S A )

Ungkapan ini dapat diperluas menjadi:

1 - R A = 1 - D A - S A + D A × S A

Dan sederhanakan seperti ini:

R A = D A + S A - D A × S A

Itu dapat direduksi menjadi satu dari dua jenis yang serupa:

R A = S A + D A × (1 - S A )

R A = D A + S A × (1 - D A )

Segera kita akan melihat bahwa yang kedua lebih sering digunakan. Menarik juga untuk dicatat bahwa nilai alpha yang dihasilkan tidak tergantung pada urutan relatif objek - opacity dari piksel yang dihasilkan adalah sama, bahkan jika Anda menukar sumber dan tujuan. Ini sangat logis. Cahaya yang melewati dua objek harus memudar dengan cara yang sama, dari sisi bintang mana pun, dari depan atau dari belakang.

Kombinasi warna


Menghitung alpha tidak begitu sulit, jadi mari kita coba memahami perhitungan komponen RGB. Gambar sumber memiliki warna S RGB , tetapi opacity S A hanya memaksa produk dari kedua nilai ini ke dalam hasil akhir:

S RGB × S A

Gambar tujuan memiliki warna D RGB , opacity membuatnya memancarkan cahaya D RGB × D A , namun, bagian dari cahaya diblokir oleh opacity gambar S, sehingga semua pengaruh tujuan sama dengan:

D RGB × D A × (1 - S A )

Kontribusi total cahaya dari S dan D sama dengan jumlah mereka:

S RGB × S A + D RGB × D A × (1 - S A )

Demikian pula, kontribusi dari layer yang digabungkan sama dengan warna mereka kali opacity mereka:

R RGB × R A

Kami ingin kedua nilai ini cocok:

R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )

Apa yang memberi kita persamaan terakhir:

R A = S A + D A × (1 - S A )

R RGB = (S RGB × S A + D RGB × D A × (1 - S A )) / R A

Lihat betapa rumitnya persamaan kedua! Perhatikan bahwa untuk mendapatkan nilai RGB dari hasil, kita perlu membaginya dengan nilai alpha. Namun, untuk tahap berikutnya dari compositin, perkalian dengan nilai alpha akan diperlukan lagi, karena hasil dari operasi saat ini akan menjadi sumber atau tujuan baru dari operasi selanjutnya. Itu hanya jelek.

Mari kita kembali ke bentuk hampir akhir R RGB sebentar:

R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )

Sumber, tujuan, dan hasil dikalikan dengan komponen alfa mereka. Ini membuat kita mengerti bahwa warna dan alpha dari pixel "suka" untuk bersama, jadi kita perlu mengambil langkah mundur dan memikirkan kembali cara kita menyimpan informasi warna.

Alfa prapultiplied


Ingatlah bahwa kita berbicara tentang opacity - jika objek sebagian buram, maka kontribusinya terhadap hasil juga akan parsial. Konsep Premultiplied alpha (“pre-multiplication by alpha”) mengimplementasikan ide ini. Nilai-nilai komponen RGB, seperti namanya, adalah pra-dikalikan dengan komponen alpha. Mari kita mulai dengan warna tanpa multiplikasi awal:

(1,00, 0,80, 0,30, 0,40)

Perkalian awal oleh alpha memberi kita hal berikut:

(0,40, 0,32, 0,12, 0,40)

Mari kita lihat beberapa piksel sekaligus. Gambar di bawah ini menunjukkan bagaimana informasi warna disimpan tanpa terlebih dahulu mengalikan alpha:


Informasi RGB dan A dalam gambar tanpa perkalian sebelumnya

Perhatikan bahwa area di mana alfa adalah 0 dapat memiliki nilai RGB sewenang-wenang, seperti yang dapat dilihat dari gangguan hijau dan biru pada gambar. Dalam kasus perkalian awal dengan alpha, informasi warna juga menyimpan nilai opacity pixel:


Informasi RGB dan A dalam gambar yang sudah dikalikan

Alfa prapultiplied kadang-kadang disebut alpha yang berasosiasi, dan alfa non-premultiplied kadang-kadang disebut alfa lurus atau tidak berhubungan.

Ketika komponen alfa warna adalah 0, perkalian awal mengatur ulang semua komponen lain, terlepas dari nilainya:

(0,0, 0,0, 0,0, 0,0)

Dalam kasus alfa yang telah ditiru sebelumnya, hanya ada satu warna yang benar-benar transparan, dan ini menawan.

Keuntungan dari pemrosesan komponen warna ini secara bertahap akan menjadi jelas bagi Anda, tetapi sebelum kita kembali ke contoh pengomposisian, mari kita lihat bagaimana alfa yang dikultipultasikan sebelumnya membantu memecahkan beberapa masalah rendering lainnya.

Penyaringan


Gaussian blur adalah cara populer untuk membuat latar belakang yang tidak fokus dengan menarik atau untuk mengurangi frekuensi tinggi bagian latar belakang dari isi beberapa elemen UI. Seperti yang akan kita lihat, pra-mengalikan alpha sangat penting untuk menciptakan blur yang tampak benar.

Gambar yang akan kita analisis dibuat dengan mengisi latar belakang dengan 1% biru buram, di mana lingkaran merah buram digambar. Pertama, mari kita lihat sebuah contoh tanpa multiplikasi awal. Saya memisahkan saluran RGB dari saluran alfa untuk memahami apa yang sedang terjadi. Tanda panah menunjukkan operasi blur:


Mengaburkan konten tanpa multiplikasi sebelumnya

Hasil akhirnya memiliki lingkaran cahaya biru jelek. Itu terjadi karena latar belakang biru bocor ke area merah selama blur, dan hanya pada saat itu , selama pengomposisian, bobot alfa ditambahkan ke dalamnya.

Ketika warna dikalikan dengan alpha, hasilnya benar:


Konten buram pra-dikalikan

Karena pra-multiplikasi, warna biru gambar dikurangi menjadi 1% dari kekuatan aslinya, sehingga pengaruhnya pada warna lingkaran kabur sangat kecil.

Interpolasi


Merender gambar yang pikselnya cocok dengan tujuan adalah tugas sederhana karena kita perlu melakukan pemetaan satu-ke-satu yang sepele antara sampel. Masalah muncul ketika tidak ada pemetaan sederhana, misalnya, karena rotasi, penskalaan, atau tanda hubung. Gambar di bawah ini menunjukkan bahwa piksel gambar yang diputar yang ditunjukkan oleh garis merah tidak lagi sesuai dengan tujuan:


Orientasi gambar relatif dan piksel tujuan sebelum dan sesudah rotasi.

Ada banyak cara untuk memilih warna dari gambar yang akan ditulis ke piksel tujuan, dan yang paling sederhana adalah yang disebut interpolasi tetangga terdekat, di mana sebagai piksel terakhir, sampel terdekat dalam tekstur dipilih secara sederhana.

Dalam demonstrasi di bawah ini, garis merah menunjukkan posisi gambar di tujuan. Sisi kanan menunjukkan posisi sampel dari sudut pandang gambar . Dengan menyeret bilah geser (dalam artikel asli), Anda dapat memutar segi empat dan mengamati bagaimana sampel memilih warna dari bitmap. Saya menyoroti satu piksel dalam sumber dan tujuan, sehingga hubungan mereka lebih jelas:


Solusi ini cukup fungsional dan pikselnya memiliki warna holistik, tetapi kualitasnya tidak dapat diterima. Akan lebih baik menggunakan interpolasi bilinear , yang menghitung rata-rata tertimbang dari empat piksel terdekat dari gambar sampel:


Ini berfungsi lebih baik, tetapi tepi di sekitar persegi panjang tidak terlihat benar, isi piksel bergabung tanpa perkalian, karena alpha "diterapkan" setelah interpolasi. Terkadang solusi yang disarankan untuk menggabungkan warna konten yang tepat, yang ditunjukkan dalam artikel menakjubkan oleh Adrian Correger [ terjemahan di Habré], jauh dari ideal - tidak ada satu warna pun di celah antara persegi panjang merah dan biru yang akan terlihat benar.

Mari kita lihat bagaimana semuanya akan terlihat dalam gambar dengan alfa yang sudah di-preultiplied dan pengomposisian dengan rumus lanjutan, yang akan segera kita dapatkan:


Sempurna - kami menyingkirkan semua perpaduan warna dan gigi tidak terlihat .

Pada akhirnya, masalah yang terkait dengan blur dan interpolasi terkait erat. Operasi apa pun yang membutuhkan kombinasi warna tembus cahaya, tanpa terlebih dahulu mengalikan warna dengan alfa, cenderung memberikan hasil yang salah.

Pengomposisian yang tepat


Mari kita kembali ke penggabungan. Kami menetapkan persamaan yang hampir diturunkan:

R RGB × R A = S RGB × S A + D RGB × D A × (1 - S A )

Jika Anda membayangkan warna menggunakan alfa yang telah ditiru sebelumnya, maka semua perkalian yang tidak nyaman ini akan hilang, karena alfa sudah menjadi bagian dari nilai warna. Lalu kami mendapatkan yang berikut:

R RGB = S RGB + D RGB × (1 - S A )

Mari kita lihat persamaan alfa:

R A = S A + D A × (1 - S A )

Koefisien untuk saluran merah, hijau, biru, dan alfa adalah sama, sehingga kami dapat mengekspresikan seluruh ekspresi dengan satu persamaan dan hanya ingat bahwa setiap komponen menjalani operasi yang sama:

R = S + D × (1 - S A )

Lihat bagaimana alpha yang di-preultiplied membuat segalanya mudah. Saat kami menganalisis komponen persamaan, semuanya ada di tempatnya. Operasi menutupi bagian dari cahaya latar belakang dan menambahkan cahaya baru:

R = S + D × (1 - S A )

Operasi pencampuran ini disebut source-over, sover atau hanya normal, dan itu tanpa diragukan lagi adalah mode penggabungan yang paling umum. Hampir semua yang Anda lihat di situs web saya tercampur dalam mode ini.

Asosiatif


Properti source-over penting yang dilakukan pada warna pra-alfa dikalikan adalah asosiatif dari operasi ini. Berkat dia, dalam persamaan pencampuran kompleks, kita dapat menempatkan tanda kurung sepenuhnya secara sewenang-wenang. Semua komposisi yang ditunjukkan di bawah ini adalah setara:

R = (((A⇨B) ⇨C) ⇨D) ⇨E

R = (A⇨B) ⇨ (C⇨ (D⇨E))

R = A⇨ (B⇨ (C⇨ (D⇨E)) )

Buktinya cukup sederhana, tapi saya tidak akan membebani Anda dengan manipulasi aljabar. Dalam praktiknya, ini berarti bahwa kita dapat membuat sebagian gambar yang kompleks tanpa takut bahwa komposisi akhir akan terlihat salah.

Dalam sebagian besar kasus, alpha digunakan hanya untuk pengomposisian menggunakan source-over, tetapi keuntungannya tidak berakhir di situ. Nilai alfa juga dapat digunakan untuk operasi rendering lain yang bermanfaat.

Porter-Duff Compositing


Pada Juli 1984, Thomas Porter dan Tom Duff menerbitkan artikel aslinya, "Compositing Digital Images . " Para penulis tidak hanya pertama-tama memperkenalkan konsep alfa yang diultipultipulasikan dan menurunkan persamaan pengompositian sumber, tetapi juga mempresentasikan seluruh keluarga operasi pengompositan alfa, banyak di antaranya sedikit diketahui, meskipun sangat bermanfaat. Fungsi baru juga disebut operator , karena, seperti menambah atau mengalikan, mereka melakukan tindakan pada nilai input untuk membuat nilai output.

Berakhir


Dalam contoh di masa mendatang, kami akan menggunakan demo interaktif yang menunjukkan operasi berbagai mode campuran. Gambar tujuan akan menjadi simbol "klub" hitam, dan gambar sumber akan menjadi simbol "cacing" merah. Anda dapat menarik hati ke atas gambar dan mengamati bagaimana bentuk tumpang tindih berperilaku di bawah berbagai operator pengomposit. Perhatikan minimap kecil di sudut. Beberapa blending mode sangat merusak dan mudah membingungkan. Minimalap selalu menunjukkan hasil pengomposisian sumber-sederhana, yang menyederhanakan pemahaman:


R = S + D × (1 - S A )

R = S × (1 - D A ) + D

Jika Anda beralih ke tujuan-lebih, maka Anda akan segera menyadari bahwa itu hanya "membalik" sumber-lebih - tujuan dan sumber ditukar dalam persamaan dan hasilnya setara dengan apa yang akan kita anggap tujuan sebagai gambar sumber. Meskipun tampak berlebihan, operator tujuan-lebih sangat berguna karena memungkinkan Anda untuk membuat objek yang berada di bawah konten yang ada.

Keluar


Pernyataan sumber-keluar dan tujuan-keluar sangat bagus untuk meninju lubang pada sumber atau tujuan:


R = S × (1 - D A )

R = D × (1 - S A )

Dari dua operator ini, Destination-out lebih nyaman karena menggunakan saluran alpha untuk membuat lubang di formulir tujuan.

Masuk


Operator source-in dan destination-in pada dasarnya adalah masking operator:


R = S × D A

R = D × S A

Mereka membuatnya cukup mudah untuk membuat persimpangan geometri nontrivial yang kompleks tanpa menyelesaikan persimpangan vektor kontur yang relatif sulit.

Di atas


Operator source-atopdan destination-atopmemungkinkan Anda untuk overlay konten baru pada yang sudah ada, sambil menyembunyikannya di sepanjang jalur tujuan:


R = S × D A + D × (1 - S A )

R = S × (1 - D A ) + D × S A

Xor


XOR operator ( xor) toko atau sumber, atau tujuan, dan daerah tumpang tindih mereka menghilang:


R = S × (1 - D A ) + D × (1 - S A )

Sumber, Tujuan, Jelas


Tiga mode pengomposisian klasik terakhir cukup membosankan. Source, juga disebut copy, hanya mengambil sumber warna. Demikian pula, ia destinationmengabaikan sumber warna dan hanya mengembalikan destination. Operator clearhanya membersihkan semuanya:


R = S

R = D

R = 0

Penerapan mode-mode ini terbatas. Dengan menggunakannya clear, Anda dapat membersihkan buffer yang terisi, tetapi operasi ini dapat dioptimalkan dengan hanya mengisi memori dengan nol. Selain itu, dalam beberapa kasus source dapat lebih ekonomis dalam perhitungan, karena tidak memerlukan pencampuran, tetapi hanya mengganti isi buffer dengan informasi sumber.

Porter Duff beraksi


Setelah berurusan dengan masing-masing operator, mari kita lihat bagaimana Anda dapat menggabungkannya. Pada contoh di bawah ini, kita akan menggambar logo laut tanpa menggunakan masking atau bentuk geometris yang kompleks. Garis biru menunjukkan geometri sederhana yang sedang dibuat. Anda dapat menelusuri langkah-langkah dengan mengklik di sisi kanan gambar, dan kembali dengan mengklik di sebelah kiri:


Tentu saja, kita sama sekali tidak berkewajiban untuk meninggalkan topeng dan memangkas kontur, tetapi kita sering melupakan alat seperti mode pengomposisian Porter-Duff, meskipun jauh lebih mudah untuk membuat beberapa efek visual dengan bantuan mereka.

Operator


Jika Anda melihat lebih dekat pada operator Porter-Duff, Anda akan melihat bahwa mereka semua memiliki bentuk yang sama. Sumber selalu dikalikan dengan faktor F S dan ditambahkan ke tujuan, dikalikan dengan faktor F D :

R = S × F S + D × F D

F S dapat mengambil nilai-nilai 0, 1, D A + 1 - D A , dan F D mungkin sama dengan 0, 1, S A atau 1 - S A . Tidak masuk akal untuk melipatgandakan sumber atau tujuan dengan alpha mereka sendiri, karena mereka sudah pra-dikalikan, dan kami hanya mendapatkan efek mewah, tetapi tidak sangat berguna dari alpha kuadrat. Semua operator dapat direpresentasikan dalam bentuk tabel:

01D A1 - D A
0jelassumbersumber-insumber-keluar
1tujuantujuan-lebih
S Atujuan-dalamtujuan-di atas
1 - S Atujuan-keluarsumber-lebihsumber di atasxor

Perhatikan simetri operator pada diagonal. Empat elemen sentral dalam tabel hilang dan itu terjadi karena mereka berbeda dari yang lain.

Pencahayaan tambahan


Dalam artikel mereka, Porter dan Duff menghadirkan operator lain di mana F S dan F D adalah 1. Dikenal dengan namanya plus, lighterdan plus-lighter:

R = S + D

Operasi ini pada dasarnya menambahkan penerangan sumber ke tujuan:


Pencahayaan tambahan diimplementasikan dengan operatorplus

Hijau dan merah dengan benar membentuk kuning, sedangkan hijau dan biru membentuk cyan. Hitam adalah tidak adanya operasi, itu tidak mengubah nilai warna dengan cara apa pun, karena menambahkan nol ke angka tidak mengubah apa pun.

Tiga operator yang tersisa tidak diberi nama karena mereka tidak terlalu berguna. Mereka hanya kombinasi masker dan campuran.

Perlu juga dicatat bahwa alfa yang di-preultipip memungkinkan kita untuk menggunakan operator dengan source-overcara yang tidak terduga. Mari kita lihat persamaannya lagi:

R = S + D × (1 - S A )

Jika kami berhasil membuat nilai alpha di sumber sama dengan nol, maka jika ada nilai-nilai non-nol di saluran RGB, kami dapat mencapai pencahayaan tambahan tanpa menggunakan operator plus:


Pencahayaan tambahan yang diterapkan menggunakan operator.source-over

Perhatikan bahwa Anda harus berhati-hati di sini - nilainya tidak lagi dikalikan dengan alpha dengan benar. Dalam beberapa program, mungkin ada optimasi yang benar-benar menghindari pencampuran warna dengan nol alpha, sementara program lain dapat membalikkan pra-kalikan dengan nilai alpha, melakukan beberapa operasi warna, dan kemudian dikalikan lagi dengan alpha, yang sepenuhnya menghancurkan saluran warna. Mungkin juga sulit untuk mengekspor sumber daya dalam format ini, jadi jika Anda tidak memiliki kendali penuh atas pipa render, maka Anda harus tetap menggunakan operator plus.

Semua elemen yang kita diskusikan sejauh ini telah dikombinasikan dengan baik. Sekarang, lepaskan kacamata merah muda kami dan diskusikan beberapa masalah yang perlu dipertimbangkan ketika bekerja dengan alpha compositing.

Keburaman grup


Mari kita lihat gambar pil sederhana ini yang terdiri dari hanya enam primitif:


Gambar pil menggunakan bentuk sederhana.

Jika kita diminta membuat pil dengan opacity 50%, maka kita mungkin tergoda untuk hanya membagi opacity menjadi setengah dari setiap operasi draw, tetapi ini akan berubah menjadi keputusan yang salah:


Hasil tak terduga dari membuat pil dengan setengah opacity.

Untuk mencapai hasil yang benar, kita tidak bisa hanya mendistribusikan opacity suatu objek ke masing-masing komponen individualnya. Pertama-tama kita perlu membuat objek, merendernya menjadi bitmap, dan baru kemudian mengubah opacity bitmap, dan akhirnya melakukan penggabungan:


Hasil yang diharapkan dari pemberian pil dengan setengah opacity.

Ini adalah kasus lain yang menunjukkan kegunaan konsep rendering ke dalam buffer sisi.

Cakupan Pengomposisian


Mengubah tutup geometrik menjadi nilai alpha tunggal memiliki konsekuensi yang tidak nyaman. Pertimbangkan kasus ketika dua sisi geometri vektor yang cocok, yang ditunjukkan pada gambar di bawah ini dengan kontur oranye dan biru, dirender menjadi bitmap. Di dunia yang ideal, hasilnya akan terlihat seperti ini, karena setiap piksel benar-benar tertutup:


Hasil render yang ideal dengan cakupan yang benar.

Namun, jika kita pertama kali membuat geometri oranye dan kemudian biru, maka pada gambar akhir sedikit latar belakang putih masih akan bocor ke piksel batas:


Hasil penggabungan dua tahap

Segera setelah lapisan disimpan dalam saluran alfa, semua informasi geometrisnya hilang, dan kami tidak dapat memulihkannya dengan cara apa pun. Geometri biru hanya bercampur dengan beberapa isi buffer, tetapi tidak tahu bahwa geometri yang diwakili oleh piksel kemerahan harus cocok dengan itu. Masalah ini terutama terlihat ketika geometri saling tumpang tindih satu sama lain. Pada gambar di bawah ini, lingkaran putih digambar di atas yang hitam. Tepi gelap terlihat, meskipun kedua lingkaran memiliki jari-jari dan posisi yang sama persis :


Lingkaran putih yang digambar di atas lingkaran hitam.

Salah satu cara untuk memperbaiki masalah ini adalah dengan tidak menghitung cakupan parsial piksel dan menggunakan buffer yang jauh lebih besar. Dengan rasterisasi vektor geometri dengan lapisan masuk / keluar sederhana, dan kemudian mengurangi skala hasil ke ukuran gambar asli, Anda dapat mencapai hasil yang diharapkan.

Namun, untuk perbandingan sempurna kualitas rendering tepi saluran alpha 8-bit, buffer harus 256 kali lebih besar di kedua arah, yaitu, jumlah piksel harus meningkat 2 16kali. Seperti yang kita lihat di atas, sambil mengurangi kedalaman bit untuk nilai cakupan, Anda masih bisa mendapatkan hasil yang memuaskan, jadi dalam praktiknya Anda dapat menggunakan skala yang lebih kecil.

Perlu juga dicatat bahwa masalah seperti itu seringkali relatif mudah dihindari tanpa menggunakan bitmap yang besar. Misalnya, alih-alih menggambar dua lingkaran yang dilapiskan, Anda bisa menggambar dua kotak di atas satu sama lain, dan kemudian menutupi hasilnya untuk membentuk lingkaran.

Nilai linear


Jika Anda telah memperbarui pengetahuan tentang ruang warna , Anda dapat mengingat bahwa sebagian besar dari mereka mengkodekan nilai warna secara non-linear, dan linierisasi awal diperlukan untuk melakukan operasi matematika yang benar. Ketika tahap ini selesai, hasil pengomposisian adalah sebagai berikut; perhatikan warna kekuningan yang indah dari bagian yang saling menempel:


Lingkaran merah fuzzy ditumpangkan dengan mengkomposisikan pada latar belakang hijau menggunakan nilai-nilai linier.

Namun, dalam kebanyakan kasus, pengomposisian tidak demikian. Cara standar untuk web dan sebagian besar perangkat lunak grafis adalah dengan langsung mencampurkan nilai-nilai non-linear:


Lingkaran merah kabur yang ditumpangkan oleh komposer pada latar belakang hijau menggunakan nilai-nilai non-linear.

Perhatikan bahwa area di mana overlay merah pada hijau jauh lebih gelap. Mereka jauh dari ideal, tetapi dalam beberapa kasus, operasi yang salah berakar dalam memahami bagaimana kita memandang warna. Misalnya, 50% abu-abu buram dari ruang sRGB terlihat persis seperti hitam murni dengan 50% opacity dicampur dengan latar belakang putih:


Pengomposisian dua warna pada latar belakang putih tanpa linierisasi

Pada gambar di bawah ini, warna sRGB gambar sumber dan tujuan dilinearisasi dan kemudian dikonversi kembali ke pengkodean non-linear untuk tampilan. Begini tampilan warna-warna ini:


Komposisi dua warna pada latar belakang putih dengan linierisasi.

Kami memiliki perbedaan yang tidak memenuhi harapan kami. Satu-satunya cara untuk mendapatkan keseragaman visual menggunakan metode ini adalah dengan memilih semua warna menggunakan nilai linier, tetapi ini sangat berbeda dari apa yang digunakan semua orang. 50% abu-abu dengan nilai linier terlihat seperti abu-abu di 73,5% dari ruang sRGB.

Selain itu, Anda harus sangat berhati-hati saat bekerja dengan alfa yang telah di-preultiplied. Pra-penggandaan harus dilakukan dengan nilai linier , yaitu sebelum coding ke non-linear. Karena ini, langkah linierisasi akan berakhir dengan benar dengan nilai linier yang benar, yang sebelumnya dikalikan dengan alpha.

Premultiplied Alpha dan Bit Depth


Meskipun memiliki kegunaan yang bagus untuk mengomposit, memfilter, dan interpolasi, alpha yang di-pra-tipipkan bukanlah “peluru perak” dan memiliki kekurangannya. Yang paling serius adalah pengurangan kedalaman warna yang bisa dibayangkan. Bayangkan penyandian 8-bit dari nilai 150, yang dikalikan dengan alpha 20%. Setelah perkalian awal dengan alpha, kita dapatkan

bulat (150 × 0,2) = 30

Jika kami mengulangi prosedur yang sama dengan nilai 151, kami mendapatkan:

bulat (151 × 0,2) = 30

Nilai yang dikodekan akan sama, meskipun ada perbedaan dalam nilai awal. Bahkan, setelah dikalikan dengan alpha, nilai 148, 149, 150, 151 dan 152 dikodekan menjadi 30, dan perbedaan asli antara lima warna unik ini hilang:


Pra-penggandaan dengan alfa 20% mengurangi berbagai nilai 8-bit menjadi satu.

Secara alami, semakin kecil alfa, semakin destruktif efeknya. Dari kisaran yang mungkin 256 4 (sekitar 4,3 miliar) dari berbagai kombinasi nilai RGBA 8-bit, setelah perkalian awal dengan alpha, hanya 25,2% yang mempertahankan representasi unik; pada kenyataannya, kita kehilangan hampir 2 bit dari kisaran 32-bit.

Untuk mengkonversi warna antara ruang warna yang berbeda, kadang-kadang perlu untuk membalikkan perkalian awal, yaitu, membagi nilai dengan komponen alfa untuk mendapatkan kecerahan warna asli. Langkah ini diperlukan karena, sebagaimana disebutkan di atas, pengkodean dilakukan secara non-linear. Adanya pra-penggandaan mengurangi keakuratan representasi warna dan konversi antara ruang warna bisa tidak sempurna.

Dalam praktiknya, mengurangi kedalaman bit jarang penting, terutama dalam penggabungan. Semakin rendah nilai alfa, semakin sedikit warna yang terlihat, dan semakin sedikit pengaruhnya pada pengomposisian. Pada akhirnya, jika Anda berusaha keras untuk operasi warna yang akurat, Anda tidak akan menggunakan representasi 8-bit - untuk tujuan ini, format jauh lebih cocok.titik mengambang .

Bacaan tambahan


Konsep saluran alpha diciptakan oleh pendiri studio Pixar, Elvy Smith dan Ed Catmell . Artikel Smith “Alpha and History of Digital Compositing” menjelaskan sejarah penemuan dan sumber-sumber nama “alpha”, serta bagaimana konsep-konsep ini berevolusi dan secara bertahap menggantikan konsep topeng dalam produksi film .

Untuk memahami arti alfa, saya sangat menyarankan Anda membaca "Interpreting Alpha" karya Andrew Glassner . Artikel ini memberikan derivasi matematis alfa yang ketat tetapi sangat mudah diakses sebagai ukuran interaksi antara opacity dan cakupan.

Diskusi terperinci tentang alfa yang telah diultipultipulasikan dapat dieksplorasi"GPU lebih suka premultiplikasi" oleh Eric Haines. Artikel ini tidak hanya memberikan gambaran yang sangat baik tentang masalah yang disebabkan oleh kurangnya perkalian awal, terutama dalam rendering 3D, tetapi juga menyediakan tautan ke banyak artikel lain tentang topik ini.

Kesimpulannya


Awalnya, artikel ini dimaksudkan sebagai penjelasan dari operator pengomposisian Porter-Duff, tetapi semua konsep lain yang terkait dengan pengomposisian alfa ternyata sangat menarik sehingga saya tidak dapat melewatkannya.

Apa yang paling saya sukai dari alpha adalah itu hanya angka tambahan yang menyertai komponen RGB, tetapi pada saat yang sama ia menciptakan banyak kemampuan rendering yang unik. Alpha benar-benar menciptakan perubahan peluang baru di dunia lama yang membosankan dari penggabungan dan rendering 2D.

Lain kali Anda melihat tepi halus bentuk vektor atau melihat overlay gelap yang menggelapkan beberapa bagian antarmuka pengguna, pikirkan komponen kecil namun kuat yang memungkinkan semuanya terjadi.

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


All Articles