Pseudo Lens Flare

Halo, Habr! Saya mempersembahkan kepada Anda terjemahan artikel "Pseudo Lens Flare" oleh John Chapman.

gambar

Lens suar (lens suar) adalah artefak fotografi yang timbul dari hamburan dan pembiasan cahaya dalam sistem lensa. Meskipun ini adalah artefak, ada banyak alasan untuk menggunakan suar lensa dalam grafik komputer:

  • itu meningkatkan kecerahan yang dirasakan dan rentang dinamis gambar yang terlihat.
  • suar lensa sering ditemukan dalam foto, sehingga ketidakhadirannya bisa mencolok
  • itu dapat memainkan peran penting dalam gaya atau drama, atau dapat menjadi bagian dari gameplay dalam game (bayangkan silau membutakan pemain)

Secara tradisional, suar lensa secara realtime telah diimplementasikan menggunakan teknologi berbasis sprite. Meskipun sprite memberikan hasil yang mudah dikontrol dan sangat realistis, mereka harus ditempatkan secara eksplisit dan membutuhkan data oklusi untuk ditampilkan dengan benar. Di sini saya akan menjelaskan efek ruang layar yang sederhana dan relatif murah yang menciptakan suar lensa semu dari buffer warna input. Ini tidak didasarkan pada fisika, sehingga hasilnya sedikit berbeda dari yang fotorealistik, tetapi dapat digunakan dalam kombinasi dengan (atau sebagai pengganti) untuk efek berbasis sprite tradisional.

Algoritma


Terdiri dari 4 tahap:

  1. Downsample / ambang batas.
  2. Generasi elemen suar lensa .
  3. Kabur
  4. Kelas atas / blending dengan gambar asli.

1. Downsample / Ambang Batas


Downsampling - optimisasi untuk mengurangi biaya langkah selanjutnya. Selain itu, kami ingin memilih subset piksel paling terang di gambar asli. Menggunakan skala / bias (skala / bias) menyediakan cara yang fleksibel untuk mencapai ini:

uniform sampler2D uInputTex; uniform vec4 uScale; uniform vec4 uBias; noperspective in vec2 vTexcoord; out vec4 fResult; void main() { fResult = max(vec4(0.0), texture(uInputTex, vTexcoord) + uBias) * uScale; } 

gambar

Penyesuaian skala / bias adalah cara utama untuk menyesuaikan efek; pengaturan terbaik akan tergantung pada rentang dinamis buffer warna, serta seberapa tipis Anda ingin melihat hasilnya. Karena fakta bahwa tekniknya adalah pendekatan, kehalusan lebih cenderung terlihat lebih baik.

2. Generasi elemen suar lensa


Elemen suar lensa cenderung berputar di tengah gambar. Dengan mensimulasikan efek ini, kita dapat memperluas hasil tahap sebelumnya secara horizontal / vertikal. Ini mudah dilakukan pada tahap pembuatan elemen dengan memperluas koordinat tekstur:

 vec2 texcoord = -vTexcoords + vec2(1.0); 

Ini tidak perlu; pembuatan elemen bekerja dengan baik dengan dan tanpa itu. Namun, hasil perubahan koordinat tekstur membantu memisahkan secara visual efek suar lensa dari gambar asli.

Hantu


" Hantu " (hantu) mengulangi sorotan yang mencerminkan area terang di buffer warna, terbentang relatif ke tengah gambar. Pendekatan yang saya pilih untuk menghasilkan adalah untuk mendapatkan vektor dari piksel saat ini ke tengah layar, dan kemudian membuat beberapa pilihan di sepanjang vektor ini.

gambar

 uniform sampler2D uInputTex; uniform int uGhosts; // number of ghost samples uniform float uGhostDispersal; // dispersion factor noperspective in vec2 vTexcoord; out vec4 fResult; void main() { vec2 texcoord = -vTexcoord + vec2(1.0); vec2 texelSize = 1.0 / vec2(textureSize(uInputTex, 0)); // ghost vector to image centre: vec2 ghostVec = (vec2(0.5) - texcoord) * uGhostDispersal; // sample ghosts: vec4 result = vec4(0.0); for (int i = 0; i < uGhosts; ++i) { vec2 offset = fract(texcoord + ghostVec * float(i)); result += texture(uInputTex, offset); } fResult = result; } 

Perhatikan bahwa saya menggunakan fract () untuk memastikan bahwa koordinat tekstur membungkus; ekuivalen Anda dapat menggunakan mode bungkus GL_REPEAT untuk tekstur.

Inilah hasilnya:

gambar

Anda dapat meningkatkan hasilnya dengan hanya mengizinkan area terang lebih dekat ke tengah gambar untuk menghasilkan hantu. Kami dapat mencapai ini dengan menambahkan bobot yang akan berkurang dari pusat sampel:

 vec4 result = vec4(0.0); for (int i = 0; i < uGhosts; ++i) { vec2 offset = fract(texcoord + ghostVec * float(i)); float weight = length(vec2(0.5) - offset) / length(vec2(0.5)); weight = pow(1.0 - weight, 10.0); result += texture(uInputTex, offset) * weight; } 

Fungsi beratnya sesederhana mungkin - linier. Alasan kami menghitung berat di dalam loop adalah karena area terang di tengah gambar input dapat "melemparkan" hantu ke perbatasan, tetapi area terang di perbatasan tidak dapat melemparkan hantu ke tengah.

gambar

Peningkatan terakhir adalah perubahan warna radial hantu, sesuai dengan tekstur 1D:

gambar

Ini diterapkan setelah siklus untuk mempengaruhi warna akhir hantu:

 result *= texture(uLensColor, length(vec2(0.5) - texcoord) / length(vec2(0.5))); 

HALOS (lingkaran cahaya)


Jika kita mengambil vektor ke tengah gambar, seperti dalam perhitungan hantu , tetapi memperbaiki panjang vektor, kita mendapatkan efek yang berbeda: gambar asli terdeformasi secara radial:

gambar
Kita dapat menggunakan ini untuk membuat "halo" dengan mengalikan berat dengan sampel, sehingga membatasi kontribusi gambar cacat ke cincin yang jari-jarinya dikendalikan oleh uHaloWidth :

 // sample halo: vec2 haloVec = normalize(ghostVec) * uHaloWidth; float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5)); weight = pow(1.0 - weight, 5.0); result += texture(uInputTex, texcoord + haloVec) * weight; 

gambar

DISTORSI KROMATIK (distorsi warna)


Beberapa suar lensa memiliki distorsi warna yang disebabkan oleh variasi dalam refraksi cahaya pada panjang gelombang yang berbeda. Kita dapat mensimulasikan ini dengan membuat fungsi yang memilih saluran merah, hijau, dan biru secara terpisah dengan offset yang sedikit berbeda di sepanjang vektor sampel:

 vec3 textureDistorted( in sampler2D tex, in vec2 texcoord, in vec2 direction, // direction of distortion in vec3 distortion // per-channel distortion factor ) { return vec3( texture(tex, texcoord + direction * distortion.r).r, texture(tex, texcoord + direction * distortion.g).g, texture(tex, texcoord + direction * distortion.b).b ); } 

Ini dapat digunakan sebagai pengganti langsung untuk tekstur panggilan () dalam daftar sebelumnya. Saya menghitung arah dan distorsi sebagai berikut:

 vec2 texelSize = 1.0 / vec2(textureSize(uInputTex, 0)); vec3 distortion = vec3(-texelSize.x * uDistortion, 0.0, texelSize.x * uDistortion); vec3 direction = normalize(ghostVec); 

Meskipun fungsi pengambilannya sederhana, biaya sampel x3 dari tekstur, meskipun mereka semua harus ramah cache kecuali Anda mengatur uDistortion ke beberapa nilai raksasa.

Dengan generasi elemen, semuanya. Inilah hasilnya:

gambar

3. Kabur


Tanpa buram, elemen suar lensa (khususnya, hantu) cenderung mempertahankan tampilan gambar. Dengan menambahkan blur ke elemen suar lensa , kami melemahkan frekuensi tinggi dan dengan demikian mengurangi kontras dengan gambar input, yang membantu kami menjual efeknya.

gambar

Saya tidak akan memberi tahu cara membuat buram; Anda dapat membacanya di berbagai sumber daya Internet (Gaussian blur).

4. Kelas atas / menyatu dengan gambar asli


Jadi, kita memiliki elemen suar lensa kita, kabur dengan baik. Bagaimana kita bisa menggabungkannya dengan gambar sumber asli? Ada beberapa pertimbangan penting mengenai seluruh pipa render:

  • Setiap kekaburan gerakan atau kedalaman bidang selanjutnya harus diterapkan sebelum digabungkan dengan suar lensa , sehingga elemen suar lensa tidak akan ikut serta dalam efek ini.
  • Suar lensa harus diterapkan sebelum melakukan pemetaan ton . Ini masuk akal secara fisik, karena tonemapping meniru respons film / CMOS terhadap cahaya yang masuk, di mana suar lensa merupakan bagian integral.

Dengan mengingat hal itu, ada beberapa hal yang dapat kita lakukan pada tahap ini untuk meningkatkan hasilnya:

LENS DIRT


Pertama, Anda perlu memodifikasi elemen suar lensa dengan tekstur kotor dalam resolusi penuh (yang banyak digunakan di Battlefield 3):

gambar

 uniform sampler2D uInputTex; // source image uniform sampler2D uLensFlareTex; // input from the blur stage uniform sampler2D uLensDirtTex; // full resolution dirt texture noperspective in vec2 vTexcoord; out vec4 fResult; void main() { vec4 lensMod = texture(uLensDirtTex, vTexcoord); vec4 lensFlare = texture(uLensFlareTex, vTexcoord) * lensMod; fResult = texture(uInputTex, vTexcoord) + lensflare; } 

Kunci untuk ini adalah tekstur yang sangat kotor pada lensa. Jika kontrasnya rendah, bentuk suar lensa cenderung mendominasi hasilnya. Dengan meningkatnya kontras, elemen suar lensa teredam, yang memberikan tampilan estetika yang berbeda dan juga menyembunyikan beberapa cacat.

STARBURST DIFFRACTION


Sebagai peningkatan tambahan, kita dapat menggunakan tekstur starburst dengan menambahkannya ke kotoran lensa :

gambar
Sebagai tekstur, starburst tidak terlihat sangat bagus. Namun demikian, kita dapat meneruskan matriks transformasi ke shader, yang akan memungkinkan kita untuk memutar / merusak starburst setiap frame dan mendapatkan efek dinamis yang diinginkan:

 uniform sampler2D uInputTex; // source image uniform sampler2D uLensFlareTex; // input from the blur stage uniform sampler2D uLensDirtTex; // full resolution dirt texture uniform sampler2D uLensStarTex; // diffraction starburst texture uniform mat3 uLensStarMatrix; // transforms texcoords noperspective in vec2 vTexcoord; out vec4 fResult; void main() { vec4 lensMod = texture(uLensDirtTex, vTexcoord); vec2 lensStarTexcoord = (uLensStarMatrix * vec3(vTexcoord, 1.0)).xy; lensMod += texture(uLensStarTex, lensStarTexcoord); vec4 lensFlare = texture(uLensFlareTex, vTexcoord) * lensMod; fResult = texture(uInputTex, vTexcoord) + lensflare; } 

Matriks transformasi uLensStarMatrix didasarkan pada nilai yang diperoleh dari orientasi kamera sebagai berikut:

 vec3 camx = cam.getViewMatrix().col(0); // camera x (left) vector vec3 camz = cam.getViewMatrix().col(1); // camera z (forward) vector float camrot = dot(camx, vec3(0,0,1)) + dot(camz, vec3(0,1,0)); 

Ada cara lain untuk mendapatkan nilai camrot; yang paling penting, itu harus berubah terus menerus ketika kamera diputar. Matriks itu sendiri dibangun sebagai berikut:

 mat3 scaleBias1 = ( 2.0f, 0.0f, -1.0f, 0.0f, 2.0f, -1.0f, 0.0f, 0.0f, 1.0f, ); mat3 rotation = ( cos(camrot), -sin(camrot), 0.0f, sin(camrot), cos(camrot), 0.0f, 0.0f, 0.0f, 1.0f ); mat3 scaleBias2 = ( 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, ); mat3 uLensStarMatrix = scaleBias2 * rotation * scaleBias1; 

Matriks skala dan bias membutuhkan offset asal tekstur sehingga kita dapat memutar starburst relatif ke tengah gambar.

Kesimpulan


Jadi sekarang semuanya! Metode ini menunjukkan bagaimana proses pos yang relatif disederhanakan memberikan suar lensa yang tampak layak. Ini tidak sepenuhnya fotorealistik, tetapi jika digunakan dengan benar, itu dapat menghasilkan hasil yang sangat baik.

gambar



UPD
Penulis juga menerbitkan artikel dengan sedikit optimasi.
Kode sumber dapat dilihat di sini dan di sini .

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


All Articles