Pelajari OpenGL. Pelajaran 5.7 - HDR


Saat menulis ke framebuffer, nilai-nilai kecerahan warna dikurangi ke interval dari 0,0 ke 1,0. Karena itu, pada pandangan pertama tidak berbahaya, kita harus selalu memilih nilai untuk pencahayaan dan warna yang sesuai dengan batasan ini. Pendekatan ini bekerja dan memberikan hasil yang layak, tetapi apa yang terjadi jika kita bertemu daerah yang sangat terang dengan banyak sumber cahaya terang, dan total kecerahan melebihi 1,0? Akibatnya, semua nilai yang lebih besar dari 1.0 akan dikonversi ke 1.0, yang tidak terlihat bagus:



Karena nilai warna dikurangi menjadi 1,0 untuk sejumlah besar fragmen, area besar gambar diisi dengan warna putih yang sama, sejumlah besar detail gambar hilang, dan gambar itu sendiri mulai terlihat tidak wajar.


Solusi untuk masalah ini mungkin dengan mengurangi kecerahan sumber cahaya sehingga tidak ada fragmen yang lebih terang dari 1,0 di atas panggung: ini bukan solusi terbaik, yang memaksa penggunaan nilai pencahayaan yang tidak realistis. Pendekatan terbaik adalah untuk memungkinkan nilai kecerahan untuk sementara melebihi kecerahan 1,0 dan pada langkah terakhir mengubah warna sehingga kecerahan kembali ke kisaran 0,0 hingga 1,0, tetapi tanpa kehilangan detail gambar.


Layar komputer mampu menampilkan warna dengan kecerahan mulai dari 0,0 hingga 1,0, tetapi kami tidak memiliki batasan seperti itu saat menghitung pencahayaan. Dengan memungkinkan warna fragmen menjadi lebih terang daripada satu kesatuan, kami mendapatkan rentang kecerahan yang jauh lebih tinggi untuk bekerja - HDR (rentang dinamis tinggi) . Dengan hdr, hal-hal yang cerah terlihat cerah, hal-hal yang gelap dapat menjadi sangat gelap, dan dengan melakukan itu kita akan melihat detailnya.



Awalnya, rentang dinamis tinggi digunakan dalam fotografi: fotografer mengambil beberapa foto identik adegan dengan eksposur berbeda, menangkap warna dari hampir semua kecerahan. Kombinasi dari foto-foto ini membentuk gambar HDR di mana sebagian besar detail menjadi terlihat karena kombinasi gambar dengan kehilangan eksposur yang berbeda. Misalnya, di bawah ini di gambar kiri fragmen gambar yang sangat terang terlihat jelas (lihat jendela), tetapi detail ini hilang saat menggunakan eksposur tinggi. Namun, pencahayaan yang tinggi membuat detail pada area gelap gambar yang tidak terlihat sebelumnya.



Ini mirip dengan cara kerja mata manusia. Dengan kurangnya cahaya, mata beradaptasi, sehingga detail gelap menjadi terlihat jelas, dan juga untuk area terang. Dapat dikatakan bahwa mata manusia memiliki kontrol eksposur otomatis, tergantung pada kecerahan pemandangan.


Render HDR bekerja dengan cara yang hampir sama. Kami mengizinkan ketika rendering menggunakan rentang besar nilai kecerahan untuk mengumpulkan informasi tentang detail terang dan gelap adegan, dan pada akhirnya kami akan mengonversi nilai dari rentang HDR kembali ke LDR (rentang dinamis rendah, berkisar dari 0 hingga 1). Transformasi ini disebut pemetaan nada , ada sejumlah besar algoritma yang bertujuan untuk mempertahankan sebagian besar detail gambar ketika mengkonversi ke LDR. Algoritma ini sering memiliki pengaturan pencahayaan yang memungkinkan mereka untuk menampilkan area gambar yang terang atau gelap dengan lebih baik.


Menggunakan HDR saat rendering memungkinkan kita tidak hanya melebihi rentang LDR dari 0 hingga 1 dan menyimpan lebih banyak detail gambar, tetapi juga memungkinkan untuk menunjukkan kecerahan nyata dari sumber cahaya. Misalnya, matahari memiliki kecerahan cahaya yang jauh lebih besar daripada sesuatu seperti senter, jadi mengapa tidak mengatur matahari untuk ini (misalnya, berikan kecerahan 10,0)? Ini akan memungkinkan kita untuk menyesuaikan pencahayaan pemandangan dengan lebih baik dengan parameter kecerahan yang lebih realistis, yang tidak mungkin dilakukan dengan rendering LDR dan rentang kecerahan dari 0 hingga 1.


Karena layar hanya menampilkan kecerahan dari 0 hingga 1, kami dipaksa untuk mengubah rentang nilai HDR yang digunakan kembali ke kisaran monitor. Cukup dengan menskalakan rentang tidak akan menjadi solusi yang baik, karena area terang akan mulai mendominasi gambar. Namun, kita dapat menggunakan berbagai persamaan atau kurva untuk mengubah nilai HDR ke LDR, yang akan memberi kita kendali penuh atas kecerahan pemandangan. Transformasi ini disebut pemetaan nada dan merupakan langkah terakhir dalam rendering HDR.


Framebuffer titik mengambang


Untuk menerapkan rendering HDR, kita perlu cara untuk mencegah nilai dibawa ke rentang dari 0 hingga 1 dari shader fragmen. Jika framebuffer menggunakan format titik tetap (GL_RGB) yang dinormalisasi untuk buffer warna, maka OpenGL secara otomatis membatasi nilai-nilai sebelum menyimpan ke framebuffer. Pembatasan ini berlaku untuk sebagian besar format framebuffer kecuali format floating point.


Untuk menyimpan nilai yang berada di luar rentang [0.0..1.0], kita dapat menggunakan buffer warna dengan format berikut: GL_RGB16F, GL_RGBA16F, GL_RGB32F or GL_RGBA32F . Ini bagus untuk rendering hdr. Buffer semacam itu akan disebut framebuffer floating point.


Membuat buffer titik mengambang berbeda dari buffer biasa hanya karena menggunakan format internal yang berbeda:


 glBindTexture(GL_TEXTURE_2D, colorBuffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); 

Framebuffer OpenGL secara default hanya menggunakan 8 bit untuk menyimpan setiap warna. Dalam framebuffer floating point dengan format GL_RGBA32F atau GL_RGBA32F , 32 bit digunakan untuk menyimpan setiap warna - 4 kali lebih banyak. Jika akurasi sangat tinggi tidak diperlukan, maka format GL_RGBA16F akan cukup memadai.


Jika buffer titik mengambang dilampirkan ke framebuffer untuk warna, kita dapat membuat adegan ke dalamnya dengan mempertimbangkan bahwa nilai warna tidak akan terbatas pada rentang dari 0 hingga 1. Dalam kode untuk artikel ini, pertama-tama kita membuat adegan ke framebuffer titik mengambang dan kemudian menampilkan konten buffer warna pada persegi panjang setengah layar. Itu terlihat seperti ini:


 glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // [...]    hdr glBindFramebuffer(GL_FRAMEBUFFER, 0); //  hdr    2     hdrShader.use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, hdrColorBufferTexture); RenderQuad(); 

Di sini nilai warna yang terkandung dalam buffer warna mungkin lebih besar dari 1. Untuk artikel ini, sebuah adegan dibuat dengan kubus memanjang besar yang terlihat seperti terowongan dengan empat titik sumber cahaya, salah satunya terletak di ujung terowongan dan memiliki kecerahan yang sangat baik.


 std::vector<glm::vec3> lightColors; lightColors.push_back(glm::vec3(200.0f, 200.0f, 200.0f)); lightColors.push_back(glm::vec3(0.1f, 0.0f, 0.0f)); lightColors.push_back(glm::vec3(0.0f, 0.0f, 0.2f)); lightColors.push_back(glm::vec3(0.0f, 0.1f, 0.0f)); 

Render ke buffer floating point persis sama seperti jika kami rendering adegan ke framebuffer biasa. Satu-satunya hal baru adalah shader hdr terfragmentasi, yang berkaitan dengan shading sederhana persegi panjang layar penuh dengan nilai-nilai dari tekstur, yang merupakan buffer warna titik-mengambang. Untuk memulai, mari menulis shader sederhana yang mentransfer data input tidak berubah:


 #version 330 core out vec4 FragColor; in vec2 TexCoords; uniform sampler2D hdrBuffer; void main() { vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb; FragColor = vec4(hdrColor, 1.0); } 

Kami mengambil input dari titik mengambang buffer warna dan menggunakannya sebagai nilai output shader. Namun, karena persegi panjang 2D dirender ke dalam framebuffer secara default, nilai-nilai output dari shader akan terbatas pada interval dari 0 hingga 1, meskipun pada kenyataannya beberapa nilai lebih besar dari 1 di beberapa tempat.



Menjadi jelas bahwa nilai warna yang terlalu besar di ujung terowongan terbatas pada kesatuan, karena sebagian besar gambar benar-benar putih, dan kami kehilangan detail gambar yang lebih terang daripada kesatuan. Karena kami menggunakan nilai HDR secara langsung sebagai LDR, ini setara dengan tidak memiliki HDR. Untuk memperbaikinya, kita harus menampilkan nilai warna yang berbeda kembali dari 0 hingga 1 tanpa kehilangan detail dalam gambar. Untuk melakukan ini, terapkan kompresi tonal.


Kompresi nada


Kompresi nada adalah konversi nilai warna agar sesuai dengan kisaran 0 hingga 1 tanpa kehilangan detail gambar, sering kali dikombinasikan dengan memberikan gambar white balance yang diinginkan.


Algoritma pemetaan nada paling sederhana dikenal sebagai algoritma pemetaan nada Reinhard . Ini menampilkan nilai HDR dalam rentang LDR. Tambahkan algoritma ini ke shader fragmen sebelumnya, dan juga terapkan koreksi gamma (dan penggunaan tekstur SRGB).


 void main() { const float gamma = 2.2; vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb; //   vec3 mapped = hdrColor / (hdrColor + vec3(1.0)); // - mapped = pow(mapped, vec3(1.0 / gamma)); FragColor = vec4(mapped, 1.0); } 

Catatan trans. - untuk nilai x yang kecil, fungsi x / (1 + x) berperilaku kira-kira seperti x, untuk x besar cenderung menyatu. Grafik fungsi:


Dengan kompresi nada Reinhardt, kami tidak lagi kehilangan detail di area gambar yang terang. Algoritma lebih memilih area yang terang, membuat area yang gelap menjadi kurang berbeda.



Di sini Anda dapat kembali melihat detail di akhir gambar, seperti tekstur kayu. Dengan algoritma yang relatif sederhana ini, kita dapat dengan jelas melihat warna dari rentang HDR dan dapat mengontrol pencahayaan adegan tanpa kehilangan detail gambar.


Perlu dicatat bahwa kita dapat menggunakan kompresi tonal langsung di akhir shader untuk menghitung pencahayaan, dan kemudian kita tidak memerlukan framebuffer floating point sama sekali. Namun, dalam adegan yang lebih kompleks, Anda akan sering menghadapi kebutuhan untuk menyimpan nilai-nilai HDR menengah dalam buffer floating point, jadi ini akan berguna.

Fitur lain yang menarik dari kompresi nada adalah penggunaan parameter eksposur. Anda mungkin ingat bahwa dalam gambar di awal artikel berbagai detail terlihat pada nilai eksposur yang berbeda. Jika kita memiliki pemandangan di mana siang dan malam berubah, masuk akal untuk menggunakan pencahayaan rendah di siang hari dan tinggi di malam hari, yang mirip dengan adaptasi mata manusia. Dengan parameter pencahayaan ini, kami dapat mengonfigurasi parameter pencahayaan yang akan berfungsi siang dan malam di bawah kondisi pencahayaan yang berbeda.


Algoritma kompresi tonal yang relatif sederhana dengan paparan seperti ini:


 uniform float exposure; void main() { const float gamma = 2.2; vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb; //     vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure); // - mapped = pow(mapped, vec3(1.0 / gamma)); FragColor = vec4(mapped, 1.0); } 

Catatan per: tambahkan grafik untuk fungsi ini dengan eksposur 1 dan 2:


Di sini kami mendefinisikan variabel untuk pencahayaan, yaitu 1 secara default dan memungkinkan kami untuk lebih akurat memilih keseimbangan antara kualitas tampilan area gelap dan terang pada gambar. Misalnya, dengan eksposur besar, kita melihat lebih detail di area gelap gambar. Sebaliknya, eksposur rendah membuat area gelap tidak dapat dibedakan, tetapi memungkinkan Anda untuk lebih baik melihat area terang pada gambar. Di bawah ini adalah gambar sebuah terowongan dengan tingkat eksposur yang berbeda.



Gambar-gambar ini jelas menunjukkan manfaat rendering hdr. Saat level eksposur berubah, kita melihat lebih detail dari adegan yang akan hilang dalam rendering normal. Ambil ujung terowongan sebagai contoh - dengan eksposur normal, tekstur pohon hampir tidak terlihat, tetapi dengan eksposur rendah, tekstur terlihat sempurna. Demikian pula, pada paparan tinggi, detail di area gelap sangat jelas terlihat.


Kode sumber untuk demo ada di sini.


Lebih banyak HDR


Kedua algoritma kompresi nada yang telah ditampilkan hanya sebagian kecil di antara sejumlah besar algoritma yang lebih maju, yang masing-masing memiliki kekuatan dan kelemahannya sendiri. Beberapa algoritma lebih baik menekankan warna / kecerahan tertentu, beberapa algoritma menunjukkan area gelap dan terang pada saat yang sama, memberikan gambar yang lebih penuh warna dan detail. Ada juga banyak metode yang dikenal sebagai penyesuaian eksposur otomatis atau adaptasi mata . Mereka menentukan kecerahan adegan di bingkai sebelumnya dan (perlahan) mengubah parameter pencahayaan, sehingga adegan gelap perlahan menjadi lebih terang, dan terang - gelap: mirip dengan pembiasaan mata manusia.


Manfaat nyata HDR paling baik dilihat pada adegan besar dan kompleks dengan algoritma pencahayaan yang serius. Untuk tujuan pelatihan, artikel ini menggunakan adegan sesederhana mungkin, karena membuat adegan besar mungkin sulit. Meskipun kesederhanaan adegan, beberapa keuntungan dari rendering hdr terlihat di sana: di area gelap dan terang gambar, detail tidak hilang, karena mereka disimpan menggunakan kompresi nada, penambahan beberapa sumber cahaya tidak mengarah pada tampilan area putih, dan nilainya tidak harus sesuai dengan LDR jangkauan.


Selain itu, rendering HDR juga membuat beberapa efek menarik lebih dapat dipercaya dan realistis. Salah satu efek ini adalah mekar, yang akan kita bahas di artikel mendatang.


Sumber daya tambahan:



PS Kami memiliki telegram conf untuk koordinasi transfer. Jika Anda memiliki keinginan serius untuk membantu penerjemahan, maka Anda dipersilakan!

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


All Articles