
Dalam
pelajaran sebelumnya, kami menyiapkan model PBR kami untuk bekerja dengan metode IBL - untuk ini kami perlu menyiapkan peta iradiasi terlebih dahulu yang menggambarkan bagian difus dari pencahayaan tidak langsung. Dalam pelajaran ini kita akan memperhatikan bagian kedua dari ekspresi reflektifitas - cermin:
Lo(p, omegao)= int limit Omega(kd fracc pi+ks fracDFG4( omegao cdotn)( omegai cdotn))Li(p, omegai)n cdot omegaid omegai
Anda mungkin memperhatikan bahwa komponen cermin Cook-Torrens (subekspresi dengan faktor
ks ) tidak konstan dan tergantung pada arah cahaya kejadian,
serta pada arah pengamatan. Solusi integral ini untuk semua kemungkinan arah kejadian cahaya, bersama dengan semua kemungkinan arah pengamatan waktu nyata, sama sekali tidak layak. Oleh karena itu, para peneliti di Epic Games telah mengusulkan pendekatan yang disebut pendekatan
jumlah terpisah , yang memungkinkan Anda untuk secara parsial menyiapkan data untuk komponen cermin terlebih dahulu, tergantung pada kondisi tertentu.
Dalam pendekatan ini, komponen cermin dari ekspresi reflektansi dibagi menjadi dua bagian, yang dapat secara individual digabungkan dan kemudian digabungkan dalam shader PBR untuk digunakan sebagai sumber radiasi specular tidak langsung. Seperti pembuatan peta iradiasi, proses konvolusi menerima peta lingkungan HDR pada inputnya.
Untuk memahami metode perkiraan jumlah-terpisah, mari kita sekali lagi melihat ekspresi reflektivitas, hanya menyisakan subekspresi untuk komponen cermin di dalamnya (bagian difus dipertimbangkan secara terpisah dalam
pelajaran sebelumnya ):
Lo(p, omegao)= int limit Omega(ks fracDFG4( omegao cdotn)( omegai cdotn)Li(p, omegai)n cdot omegaid omegai= int limit Omegafr(p, omegai, omegao)Li(p, omegai)n cdot omegaid omegai
Seperti halnya persiapan peta iradiasi, integral ini tidak mungkin diselesaikan secara real time. Oleh karena itu, diinginkan untuk menghitung peta untuk komponen cermin dari ekspresi reflektifitas, dan dalam siklus render utama, lakukan seleksi sederhana dari peta ini berdasarkan yang normal ke permukaan. Namun, semuanya tidak begitu sederhana: peta iradiasi diperoleh relatif mudah karena fakta bahwa integral hanya bergantung pada
omegai , dan subekspresi konstan untuk komponen difusi Lambertian dapat dikeluarkan dari tanda integral. Dalam hal ini, integral tidak hanya bergantung pada
omegai yang mudah dimengerti dari rumus BRDF:
fr(p,wi,wo)= fracDFG4( omegao cdotn)( omegai cdotn)
Ungkapan di bawah integral juga tergantung pada
omegao - untuk vektor dua arah, hampir tidak mungkin untuk memilih dari peta kubik yang disiapkan sebelumnya. Posisi titik
p dalam hal ini, Anda tidak dapat memperhitungkan - mengapa hal ini dibahas dalam pelajaran sebelumnya. Penghitungan awal integral untuk semua kemungkinan kombinasi
omegai dan
omegao mustahil dalam tugas waktu nyata.
Metode jumlah split dari Epic Games memecahkan masalah ini dengan membagi masalah perhitungan pendahuluan menjadi dua bagian independen, yang hasilnya dapat digabungkan nanti untuk mendapatkan nilai akhir yang dihitung. Metode split sum mengekstrak dua integral dari ekspresi asli untuk komponen mirror:
Lo(p, omegao)= int limit OmegaLi(p, omegai)d omegaiβ int limit Omegafr(p, omegai, omegao)n cdot omegaid omegai
Hasil perhitungan bagian pertama biasanya disebut
peta lingkungan pra-disaring , dan itu adalah peta lingkungan yang mengalami proses konvolusi yang ditentukan oleh ungkapan ini. Semua ini mirip dengan proses mendapatkan peta iradiasi, tetapi dalam kasus ini, konvolusi dilakukan dengan memperhitungkan nilai kekasaran. Nilai kekasaran yang lebih tinggi menyebabkan penggunaan vektor pengambilan sampel yang lebih berbeda dalam proses konvolusi, yang menghasilkan hasil yang lebih kabur. Hasil konvolusi untuk setiap tingkat kekasaran terpilih berikutnya disimpan di level mip berikutnya dari peta lingkungan yang disiapkan. Misalnya, peta lingkungan, yang dikonvolusi untuk lima tingkat kekasaran yang berbeda, berisi lima tingkat mip dan terlihat seperti ini:
Vektor sampel dan penyebarannya ditentukan berdasarkan fungsi distribusi normal (
NDF ) dari model BRDF Cook-Torrens. Fungsi ini menerima vektor normal dan arah pengamatan sebagai parameter input. Karena arah pengamatan tidak diketahui sebelumnya pada saat perhitungan pendahuluan, para pengembang Epic Games harus membuat satu asumsi lagi: arah pandangan (dan karenanya arah refleksi specular) selalu identik dengan arah keluaran sampel.
omegao . Dalam bentuk kode:
vec3 N = normalize(w_o); vec3 R = N; vec3 V = R;
Dalam kondisi seperti itu, arah pandangan tidak diperlukan selama lilitan peta lingkungan, yang membuat perhitungan layak secara real time. Tetapi di sisi lain, kita kehilangan distorsi karakteristik pantulan specular ketika diamati pada sudut akut ke permukaan reflektif, seperti dapat dilihat pada gambar di bawah ini (dari
Moving Frostbite ke PBR ). Secara umum, kompromi semacam itu dianggap dapat diterima.
Bagian kedua dari ekspresi split-sum berisi BRDF dari ekspresi asli untuk komponen mirror. Dengan asumsi bahwa kecerahan energi yang masuk secara spektral diwakili oleh cahaya putih untuk semua arah (mis.,
L(p,x)=1.0 ), dimungkinkan untuk pra-menghitung nilai untuk BRDF dengan parameter input berikut: kekasaran material dan sudut antara normal
n dan arah cahaya
omegai (atau
n cdot omegai ) Pendekatan Epic Games melibatkan penyimpanan hasil perhitungan BRDF untuk setiap kombinasi kekasaran dan sudut antara normal dan arah cahaya dalam bentuk tekstur dua dimensi yang dikenal sebagai
peta integrasi BRDF , yang kemudian digunakan sebagai
tabel pencarian (
LUT ). . Tekstur referensi ini menggunakan saluran output merah dan hijau untuk menyimpan skala dan offset untuk menghitung koefisien Fresnel dari permukaan, yang pada akhirnya memungkinkan kita untuk menyelesaikan bagian kedua dari ekspresi untuk jumlah yang terpisah:

Tekstur bantu ini dibuat sebagai berikut: koordinat tekstur horizontal (mulai dari [0., 1.]) dianggap sebagai nilai parameter input
n cdot omegai Fungsi BRDF; koordinat tekstur vertikal dianggap sebagai nilai input kekasaran.
Akibatnya, dengan memiliki peta integrasi dan peta lingkungan yang telah diproses sebelumnya, Anda dapat menggabungkan sampel dari mereka untuk mendapatkan nilai akhir dari ekspresi integral dari komponen mirror:
float lod = getMipLevelFromRoughness(roughness); vec3 prefilteredColor = textureCubeLod(PrefilteredEnvMap, refVec, lod); vec2 envBRDF = texture2D(BRDFIntegrationMap, vec2(NdotV, roughness)).xy; vec3 indirectSpecular = prefilteredColor * (F * envBRDF.x + envBRDF.y)
Peninjauan metode pemisahan-jumlah dari Epic Games ini akan membantu memberi Anda kesan tentang proses perkiraan porsi ekspresi reflektansi yang bertanggung jawab untuk komponen cermin. Sekarang mari kita coba persiapkan data kartu kita sendiri.
Pra-filtering peta lingkungan HDR
Pra-filtering peta lingkungan mirip dengan apa yang dilakukan untuk mendapatkan peta iradiasi. Satu-satunya perbedaan adalah bahwa sekarang kita memperhitungkan kekasaran dan menyimpan hasilnya untuk setiap tingkat kekasaran di tingkat mip baru dari peta kubik.
Pertama, Anda harus membuat peta kubik baru yang akan berisi hasil pra-penyaringan. Untuk membuat jumlah level mip yang diperlukan, kami cukup memanggil
glGenerateMipmaps () - memori yang diperlukan akan dialokasikan untuk tekstur saat ini:
unsigned int prefilterMap; glGenTextures(1, &prefilterMap); glBindTexture(GL_TEXTURE_CUBE_MAP, prefilterMap); for (unsigned int i = 0; i < 6; ++i) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 128, 128, 0, GL_RGB, GL_FLOAT, nullptr); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
Harap perhatikan: karena pemilihan dari
prefilterMap akan didasarkan pada keberadaan level mip, perlu untuk mengatur mode filter reduksi ke
GL_LINEAR_MIPMAP_LINEAR untuk mengaktifkan pemfilteran trilinear. Gambar pra-diproses dari gambar cermin disimpan dalam wajah yang terpisah dari peta kubik dengan resolusi di level mip dasar hanya 128x128 piksel. Untuk sebagian besar material, ini cukup, namun, jika pemandangan Anda memiliki peningkatan permukaan yang halus dan mengkilap (misalnya, mobil baru), Anda mungkin perlu meningkatkan resolusi ini.
Dalam pelajaran sebelumnya, kami menggabungkan peta lingkungan dengan membuat vektor sampel yang didistribusikan secara merata di belahan bumi
Omega menggunakan koordinat bola. Untuk mendapatkan iradiasi, metode ini cukup efektif, yang tidak dapat dikatakan tentang perhitungan refleksi specular. Fisika sorotan specular memberi tahu kita bahwa arah cahaya yang dipantulkan secara specular berbatasan dengan vektor pantulan
r untuk permukaan normal
n bahkan jika kekasarannya tidak nol:
Bentuk umum dari kemungkinan arah keluar refleksi disebut
lobus specular (
specular lobe ; "kelopak pola radiasi cermin" - mungkin terlalu bertulang,
kira-kira Per. ). Dengan peningkatan kekasaran, kelopak tumbuh dan mengembang. Juga, bentuknya berubah tergantung pada arah kejadian cahaya. Dengan demikian, bentuk kelopak sangat tergantung pada sifat-sifat bahan.
Kembali ke model microsurfaces, kita dapat membayangkan bentuk cuping cermin sebagai menggambarkan orientasi refleksi relatif terhadap vektor median permukaan mikro, dengan mempertimbangkan beberapa arah tertentu dari insiden cahaya. Memahami bahwa sebagian besar sinar cahaya yang dipantulkan terletak di dalam kelopak cermin yang berorientasi pada vektor median, masuk akal untuk membuat vektor sampel yang berorientasi dengan cara yang sama. Kalau tidak, banyak dari mereka akan menjadi tidak berguna. Pendekatan ini disebut
sampling penting .
Integrasi Monte Carlo dan pengambilan sampel penting
Untuk sepenuhnya memahami arti sampel dalam hal signifikansi, Anda harus terlebih dahulu membiasakan diri dengan alat matematika seperti metode integrasi Monte Carlo. Metode ini didasarkan pada kombinasi statistik dan teori probabilitas dan membantu memecahkan masalah statistik secara numerik pada sampel besar tanpa perlu mempertimbangkan
setiap elemen sampel ini.
Misalnya, Anda ingin menghitung pertumbuhan populasi rata-rata suatu negara. Untuk mendapatkan hasil yang akurat dan dapat diandalkan, seseorang harus mengukur pertumbuhan
setiap warga negara dan rata-rata hasilnya. Namun, karena populasi sebagian besar negara cukup besar, pendekatan ini praktis tidak dapat direalisasikan, karena memerlukan terlalu banyak sumber daya untuk pelaksanaannya.
Pendekatan lain adalah membuat subsampel yang lebih kecil yang diisi dengan elemen yang benar-benar acak (tidak bias) dari sampel asli. Selanjutnya, Anda juga mengukur pertumbuhan dan rata-rata hasil untuk subsampel ini. Anda dapat mengambil setidaknya seratus orang dan mendapatkan hasilnya, meskipun tidak sepenuhnya akurat, tetapi masih cukup dekat dengan situasi sebenarnya. Penjelasan untuk metode ini terletak pada pertimbangan hukum sejumlah besar. Dan esensinya dijelaskan dengan cara ini: hasil beberapa pengukuran dalam ukuran sampel yang lebih kecil
N , terdiri dari elemen acak dari set asli, akan dekat dengan hasil kontrol dari pengukuran yang diambil pada seluruh set awal. Selain itu, hasil perkiraan cenderung benar dengan pertumbuhan
N .
Integrasi Monte Carlo adalah penerapan hukum sejumlah besar untuk menyelesaikan integral. Alih-alih memecahkan integral, dengan mempertimbangkan seluruh set nilai (mungkin tak terbatas)
x kami menggunakan
N titik sampel acak dan rata-rata hasilnya. Dengan pertumbuhan
N hasil perkiraan dijamin mendekati solusi tepat dari integral.
O= int limitbaf(x)dx= frac1N sumNβ1i=0 fracf(x)pdf(x)
Untuk menyelesaikan integral, kami memperoleh nilai integand untuk
N titik acak dari sampel dalam [a, b], hasilnya dirangkum dan dibagi dengan jumlah total poin yang diambil untuk rata-rata. Barang
pdf menggambarkan
fungsi kerapatan probabilitas , yang menunjukkan probabilitas dengan mana setiap nilai yang dipilih terjadi dalam sampel asli. Misalnya, fungsi ini untuk pertumbuhan warga akan terlihat seperti ini:
Dapat dilihat bahwa saat menggunakan titik pengambilan sampel acak, kami memiliki peluang yang jauh lebih tinggi untuk memenuhi nilai pertumbuhan 170 cm daripada seseorang dengan pertumbuhan 150 cm.
Jelas bahwa selama integrasi Monte Carlo, beberapa titik sampel lebih cenderung muncul dalam urutan daripada yang lain. Oleh karena itu, dalam ekspresi apa pun untuk estimasi Monte Carlo, kami membagi atau mengalikan nilai yang dipilih dengan probabilitas kemunculannya menggunakan fungsi kepadatan probabilitas. Saat ini, ketika mengevaluasi integral, kami menciptakan banyak titik sampel yang terdistribusi secara merata: peluang mendapatkan salah satu dari mereka adalah sama. Dengan demikian, estimasi kami tidak
bias , yang berarti bahwa ketika jumlah titik sampel meningkat, estimasi kami akan bertemu dengan solusi yang tepat dari integral.
Namun, ada fungsi evaluasi yang
bias , yaitu menyiratkan penciptaan titik pengambilan sampel tidak dengan cara yang benar-benar acak, tetapi dengan dominasi beberapa besaran atau arah. Fungsi evaluasi semacam itu memungkinkan estimasi Monte Carlo untuk menyatu ke solusi yang tepat
lebih cepat . Di sisi lain, karena bias fungsi evaluasi, solusinya mungkin tidak pernah bertemu. Dalam kasus umum, ini dianggap kompromi yang dapat diterima, terutama dalam masalah grafik komputer, karena perkiraannya sangat dekat dengan hasil analitis dan tidak diperlukan jika pengaruhnya secara visual terlihat cukup andal. Karena kita akan segera melihat sampel dengan signifikansi (menggunakan fungsi estimasi bias) memungkinkan Anda untuk membuat poin sampel yang bias menuju arah tertentu, yang diperhitungkan dengan mengalikan atau membagi setiap nilai yang dipilih dengan nilai yang sesuai dari fungsi kepadatan probabilitas.
Integrasi Monte Carlo cukup umum dalam masalah grafik komputer, karena ini adalah metode yang cukup intuitif untuk memperkirakan nilai integral kontinu dengan metode numerik, yang cukup efektif. Cukup dengan mengambil area atau volume tempat sampel diambil (misalnya, belahan bumi kita)
Omega ), buat
N titik pengambilan sampel acak terletak di dalam, dan melakukan penjumlahan tertimbang dari nilai yang diperoleh.
Metode Monte Carlo adalah topik diskusi yang sangat luas, dan di sini kita tidak akan lagi merinci, tetapi ada satu detail yang lebih penting: tidak ada cara untuk membuat
sampel acak . Secara default, setiap titik sampel benar-benar (psvedo) acak - yang kami harapkan. Tetapi, dengan menggunakan sifat-sifat tertentu dari urutan kuasi-acak, dimungkinkan untuk membuat set vektor yang, meskipun acak, memiliki sifat yang menarik. Misalnya, saat membuat sampel acak untuk proses integrasi, Anda dapat menggunakan apa yang disebut
urutan perbedaan rendah , yang memastikan keacakan titik pengambilan sampel yang dibuat, tetapi dalam rangkaian umum, sampel tersebut
didistribusikan secara lebih merata:
Menggunakan urutan ketidakcocokan yang rendah untuk membuat satu set vektor sampel untuk proses integrasi adalah
metode intergrasi Quasi-Monte Carlo . Metode quasi Monte Carlo menyatu jauh lebih cepat daripada pendekatan umum, yang merupakan properti yang sangat menarik untuk aplikasi dengan persyaratan kinerja tinggi.
Jadi, kita tahu tentang metode umum dan kuasi-Monte Carlo, tetapi ada satu detail lagi yang akan memberikan tingkat konvergensi yang lebih besar: sampel dengan signifikansi.
Seperti yang telah disebutkan dalam pelajaran, untuk pantulan specular arah cahaya yang dipantulkan tertutup dalam lobus specular, ukuran dan bentuk yang tergantung pada kekasaran permukaan pantulan. Memahami bahwa vektor sampel acak (kuasi) apa pun yang berada di luar cuping cermin tidak akan memengaruhi ekspresi integral komponen cermin, mis. tidak berguna. Masuk akal untuk memfokuskan generasi vektor sampel di wilayah cuping cermin menggunakan fungsi estimasi bias untuk metode Monte Carlo.
Ini adalah esensi dari pengambilan sampel secara signifikan: pembuatan vektor-vektor pengambilan sampel terlampir di area tertentu yang berorientasi sepanjang vektor median permukaan mikro, dan bentuknya ditentukan oleh kekasaran material. Dengan menggunakan kombinasi metode kuasi Monte Carlo, urutan ketidakcocokan yang rendah dan bias dalam proses pembuatan vektor sampel karena pengambilan sampel secara signifikan, kami mencapai tingkat konvergensi yang sangat tinggi. Karena konvergensi ke solusi cukup cepat, kita dapat menggunakan sejumlah kecil vektor sampel untuk mencapai perkiraan yang cukup dapat diterima. Kombinasi metode yang dijelaskan, pada prinsipnya, memungkinkan aplikasi grafis untuk bahkan menyelesaikan integral komponen cermin secara real time, meskipun perhitungan awal masih tetap merupakan pendekatan yang jauh lebih menguntungkan.
Urutan ketidakcocokan rendah
Dalam pelajaran ini, kita masih menggunakan perhitungan pendahuluan komponen cermin dari ekspresi reflektansi untuk radiasi tidak langsung. Dan kita akan menggunakan sampel signifikansi menggunakan urutan acak ketidakcocokan rendah dan metode kuasi Monte Carlo. Urutan yang digunakan dikenal sebagai
urutan Hammersley , deskripsi rinci yang diberikan oleh
Holger Dammertz . Urutan ini, pada gilirannya, didasarkan pada urutan
van der Corput , yang menggunakan transformasi biner khusus dari fraksi desimal relatif terhadap titik desimal.
Menggunakan trik aritmatika bitwise yang rumit, Anda dapat secara efisien mengatur urutan van der Corpute langsung di shader dan berdasarkan itu membuat elemen ke-i dari urutan Hammersley dari pilihan di
N item:
float RadicalInverse_VdC(uint bits) { bits = (bits << 16u) | (bits >> 16u); bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } // ---------------------------------------------------------------------------- vec2 Hammersley(uint i, uint N) { return vec2(float(i)/float(N), RadicalInverse_VdC(i)); }
Hammersley () mengembalikan elemen h dari urutan ketidakcocokan rendah dari beberapa sampel ukuran
N .
Tidak semua driver OpenGL mendukung operasi bitwise (WebGL dan OpenGL ES 2.0, misalnya), jadi untuk lingkungan tertentu, implementasi alternatif penggunaannya mungkin diperlukan:
float VanDerCorpus(uint n, uint base) { float invBase = 1.0 / float(base); float denom = 1.0; float result = 0.0; for(uint i = 0u; i < 32u; ++i) { if(n > 0u) { denom = mod(float(n), 2.0); result += denom * invBase; invBase = invBase / 2.0; n = uint(float(n) / 2.0); } } return result; } // ---------------------------------------------------------------------------- vec2 HammersleyNoBitOps(uint i, uint N) { return vec2(float(i)/float(N), VanDerCorpus(i, 2u)); }
Saya perhatikan bahwa karena batasan tertentu pada operator siklus di perangkat keras lama, implementasi ini melewati semua 32 bit. Akibatnya, versi ini tidak seproduktif opsi pertama - tetapi bekerja pada perangkat keras apa pun, dan bahkan tanpa adanya operasi bit.
Pentingnya Sampel dalam Model GGX
Alih-alih distribusi seragam atau acak (Monte Carlo) vektor sampel yang dihasilkan dalam belahan bumi
Omega , yang muncul di bagian integral yang sedang kami selesaikan, kami akan mencoba membuat vektor sehingga mereka tertarik ke arah utama pantulan cahaya, ditandai oleh vektor median permukaan mikro dan tergantung pada kekasaran permukaan. Proses pengambilan sampel itu sendiri akan mirip dengan yang sebelumnya dipertimbangkan: membuka siklus dengan jumlah iterasi yang cukup besar, membuat elemen dari urutan ketidakcocokan yang rendah, berdasarkan itu kami membuat vektor pengambilan sampel dalam ruang singgung, mentransfer vektor ini ke koordinat dunia dan menggunakan kecerahan energi dari adegan untuk sampel. Pada prinsipnya, perubahan hanya berkaitan dengan fakta bahwa sekarang elemen dari urutan ketidakcocokan rendah digunakan untuk menentukan vektor sampel baru:
const uint SAMPLE_COUNT = 4096u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT);
Selain itu, untuk pembentukan lengkap vektor sampel, akan perlu mengarahkannya ke arah lobus cermin yang sesuai dengan tingkat kekasaran tertentu. Anda dapat mengambil NDF (fungsi distribusi normal) dari
pelajaran teori dan bergabung dengan GGX NDF untuk metode menentukan vektor sampel dalam kepenulisan bola Epic Games:
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) { float a = roughness*roughness; float phi = 2.0 * PI * Xi.x; float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
Hasilnya adalah vektor sampel, kira-kira berorientasi sepanjang vektor median microsurfaces, untuk kekasaran yang diberikan dan elemen dari urutan
Xi ketidakcocokan rendah. Perhatikan bahwa Epic Games menggunakan kuadrat dari nilai kekasaran untuk kualitas visual yang lebih besar, berdasarkan karya asli Disney pada metode PBR.
Setelah menyelesaikan implementasi urutan Hammersley dan kode pembuatan vektor sampel, kami dapat memberikan kode shader pra-penyaringan dan konvolusi:
#version 330 core out vec4 FragColor; in vec3 localPos; uniform samplerCube environmentMap; uniform float roughness; const float PI = 3.14159265359; float RadicalInverse_VdC(uint bits); vec2 Hammersley(uint i, uint N); vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); void main() { vec3 N = normalize(localPos); vec3 R = N; vec3 V = R; const uint SAMPLE_COUNT = 1024u; float totalWeight = 0.0; vec3 prefilteredColor = vec3(0.0); for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(dot(N, L), 0.0); if(NdotL > 0.0) { prefilteredColor += texture(environmentMap, L).rgb * NdotL; totalWeight += NdotL; } } prefilteredColor = prefilteredColor / totalWeight; FragColor = vec4(prefilteredColor, 1.0); }
Kami melakukan penyaringan awal peta lingkungan berdasarkan beberapa kekasaran yang diberikan, tingkat yang berubah untuk setiap tingkat mip dari peta kubik yang dihasilkan (dari 0,0 ke 1,0), dan hasil filter disimpan dalam variabel PreilteredColor . Selanjutnya, variabel dibagi dengan berat total untuk seluruh sampel, dan sampel dengan kontribusi yang lebih kecil ke hasil akhir (memiliki nilai NdotL lebih rendah ) juga meningkatkan berat total lebih sedikit.Menyimpan data pra-penyaringan di tingkat mip
Tetap menulis kode yang secara langsung menginstruksikan OpenGL untuk memfilter peta lingkungan dengan berbagai tingkat kekasaran dan kemudian menyimpan hasilnya dalam serangkaian level mip dari peta kubik target. Di sini, kode yang sudah disiapkan dari pelajaran tentang perhitungan peta iradiasi berguna : prefilterShader.use(); prefilterShader.setInt("environmentMap", 0); prefilterShader.setMat4("projection", captureProjection); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); unsigned int maxMipLevels = 5; for (unsigned int mip = 0; mip < maxMipLevels; ++mip) {
Prosesnya mirip dengan lilitan peta iradiasi, tetapi kali ini Anda harus menentukan ukuran buffer bingkai pada setiap langkah, menguranginya setengah untuk mencocokkan tingkat mip. Selain itu, level mip yang akan dijalankan saat ini harus ditentukan sebagai parameter ke fungsi glFramebufferTexture2D () .Hasil dari pelaksanaan kode ini harus berupa peta kubik berisi gambar refleksi yang semakin buram pada setiap level mip berikutnya. Anda dapat menggunakan peta kubik seperti itu sebagai sumber data untuk skybox dan mengambil sampel dari level mip di bawah nol: vec3 envColor = textureLod(environmentMap, WorldPos, 1.2).rgb;
Hasil dari tindakan ini adalah gambar berikut:Sepertinya peta lingkungan sumber yang sangat kabur. Jika hasil Anda serupa, maka, kemungkinan besar, proses penyaringan awal peta lingkungan HDR dilakukan dengan benar. Cobalah bereksperimen dengan sampel level mip yang berbeda dan amati peningkatan blur bertahap pada setiap level berikutnya.Pra-filter artefak konvolusi
Untuk sebagian besar tugas, pendekatan yang dijelaskan bekerja dengan sangat baik, tetapi cepat atau lambat Anda harus bertemu dengan berbagai artefak yang dihasilkan oleh proses pra-penyaringan. Berikut adalah metode dan cara paling umum untuk menghadapinya.Manifestasi jahitan peta kubik
Pemilihan nilai dari peta potong kubus yang diproses oleh filter pendahuluan untuk permukaan dengan kekasaran tinggi menyebabkan pembacaan data dari tingkat mip di suatu tempat yang lebih dekat ke ujung rantai mereka. Saat mengambil sampel dari peta kubik, OpenGL secara default tidak menyisipkan secara linear di antara wajah-wajah peta kubik. Karena level mip yang tinggi memiliki resolusi yang lebih rendah, dan peta lingkungan dilibatkan dengan mempertimbangkan lobus cermin yang jauh lebih besar, ketiadaan penyaringan tekstur antara wajah menjadi jelas:Untungnya, OpenGL memiliki kemampuan untuk mengaktifkan pemfilteran ini dengan bendera sederhana: glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
Sudah cukup untuk mengatur bendera di suatu tempat dalam kode inisialisasi aplikasi dan artefak ini dihilangkan.Titik terang muncul
Karena pantulan cermin pada kasus umum mengandung detail frekuensi tinggi, serta wilayah dengan kecerahan yang sangat berbeda, konvolusi mereka membutuhkan penggunaan sejumlah besar titik sampel untuk memperhitungkan dengan benar penyebaran besar nilai-nilai di dalam pantulan HDR dari lingkungan. Dalam contoh, kami sudah mengambil sampel dalam jumlah yang cukup besar, tetapi untuk adegan tertentu dan tingkat kekasaran material yang tinggi, ini masih belum cukup, dan Anda akan menyaksikan penampilan banyak tempat di sekitar area terang:Anda selanjutnya dapat meningkatkan jumlah sampel, tetapi ini tidak akan menjadi solusi universal, dan dalam beberapa kondisi itu masih akan memungkinkan artefak. Tetapi Anda dapat beralih ke metode Chetan Jags , yang memungkinkan Anda untuk mengurangi manifestasi artefak. Untuk melakukan ini, pada tahap konvolusi awal, pemilihan dari peta lingkungan tidak dilakukan secara langsung, tetapi dari salah satu level mipnya, berdasarkan nilai yang diperoleh dari fungsi distribusi probabilitas integrand dan kekasaran: float D = DistributionGGX(NdotH, roughness); float pdf = (D * NdotH / (4.0 * HdotV)) + 0.0001;
Ingatlah untuk mengaktifkan pemfilteran trilinear untuk peta lingkungan agar berhasil memilih dari level mip: glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
Juga, jangan lupa untuk langsung membuat level mip untuk tekstur menggunakan OpenGL, tetapi hanya setelah level mip utama sepenuhnya terbentuk:
Metode ini bekerja sangat baik, menghilangkan hampir semua (dan seringkali semua) spot pada peta yang difilter, bahkan pada tingkat kekasaran yang tinggi.Perhitungan awal BRDF
Jadi, kami telah berhasil memproses peta lingkungan dengan filter dan sekarang kami dapat berkonsentrasi pada bagian kedua perkiraan dalam bentuk jumlah terpisah yang mewakili BRDF. Untuk menyegarkan memori, lihat kembali catatan lengkap dari solusi perkiraan:L o ( p , Ο o ) = β« Ξ© L i ( p , Ο i ) d Ο i β β« Ξ© f r ( p , Ο i , Ο o, n ) n β
Ο i d Ο i
Kami sebelumnya menghitung bagian kiri dari jumlah dan mencatat hasilnya untuk berbagai tingkat kekasaran dalam peta kubik yang terpisah. Sisi kanan akan membutuhkan konvolusi ekspresi BDRF bersama dengan parameter berikut: anglen β
Ο i , kekasaran permukaan dan koefisien FresnelF 0 .
Proses yang mirip dengan mengintegrasikan BRDF cermin untuk lingkungan yang sepenuhnya putih atau dengan kecerahan energi yang konstan L i = 1.0 .
Menggabungkan BRDF untuk tiga variabel bukanlah tugas yang sepele, tetapi dalam kasus ini F 0 dapat diturunkan dari ekspresi yang menggambarkan mirror BRDF:β« Ξ© fr(p,Οi,Οo)nβ
ΟidΟi= β« Ξ© fr(p,Οi,Οo) F ( Ο o , h )F ( Ο o , h ) nβ
ΟidΟi
Di sini F adalah fungsi yang menggambarkan perhitungan set Fresnel. Memindahkan pembagi ke dalam ekspresi untuk BRDF, Anda dapat pergi ke notasi setara berikut:β« Ξ© f r ( p , Ο i , Ο o )F ( Ο o , h ) F(Οo,h)nβ
ΟidΟi
Memasang Kembali Entri yang Tepat F pada pendekatan Fresnel-Schlick, kita mendapatkan:β« Ξ© f r ( p , Ο i , Ο o )F ( Ο o , h ) (F0+(1-F0)(1-Οoβ
h)5)nβ
ΟidΟi
Nyatakan ekspresi ( 1 - Ο o β
h ) 5 bagaimana
/ a l p h a untuk menyederhanakan keputusanF 0 :
β«Ξ©fr(p,Οi,Οo)F(Οo,h)(F0+(1βF0)Ξ±)nβ
ΟidΟi
β«Ξ©fr(p,Οi,Οo)F(Οo,h)(F0+1βΞ±βF0βΞ±)nβ
ΟidΟi
β«Ξ©fr(p,Οi,Οo)F ( Ο o , h ) (F0β(1-Ξ±)+Ξ±)nβ
ΟidΟi
Fungsi selanjutnya Kami membagi F menjadi dua integral:β« Ξ© f r ( p , Ο i , Ο o )F ( Ο o , h ) (F0β(1-Ξ±))nβ
ΟidΟi+β«Ξ©fr(p,Οi,Οo)F ( Ο o , h ) (Ξ±)nβ
ΟidΟi
Dengan cara ini F 0 akan konstan di bawah integral, dan kita bisa mengeluarkannya dari tanda integral. Selanjutnya, kami akan mengungkapkanΞ± ke dalam ekspresi asli dan dapatkan entri akhir untuk BRDF sebagai jumlah terpisah:F0β«Ξ©fr(p,Οi,Οo)(1β(1βΟoβ
h)5)nβ
ΟidΟi+β«Ξ©fr(p,Οi,Οo)(1βΟoβ
h)5nβ
ΟidΟi
Dua integral yang dihasilkan mewakili skala dan offset untuk nilai F 0, masing-masing. Catat ituf ( p , Ο i , Ο o ) berisi suatu kejadianF , karena kejadian ini membatalkan satu sama lain dan menghilang dari ekspresi. Menggunakan pendekatan yang sudah dikembangkan, konvolusi BRDF dapat dilakukan bersama dengan data input: kekasaran dan sudut antara vektorn dan
w o .
Menulis hasil dalam 2D tekstur - kartu kompleksasi BRDF ( BRDF integrasi peta ), yang akan berfungsi sebagai tambahan nilai tabel untuk digunakan dalam shader final, yang akan membentuk hasil akhir dari pencahayaan specular tidak langsung.Shader konvolusi BRDF bekerja di pesawat, langsung menggunakan koordinat tekstur dua dimensi sebagai parameter input dari proses konvolusi ( NdotV dan kekasaran ). Kode ini terlihat mirip dengan konvolusi pra-penyaringan, tetapi di sini vektor sampel diproses dengan mempertimbangkan fungsi geometris BRDF dan ekspresi dari perkiraan Fresnel-Schlick: vec2 IntegrateBRDF(float NdotV, float roughness) { vec3 V; Vx = sqrt(1.0 - NdotV*NdotV); Vy = 0.0; Vz = NdotV; float A = 0.0; float B = 0.0; vec3 N = vec3(0.0, 0.0, 1.0); const uint SAMPLE_COUNT = 1024u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(Lz, 0.0); float NdotH = max(Hz, 0.0); float VdotH = max(dot(V, H), 0.0); if(NdotL > 0.0) { float G = GeometrySmith(N, V, L, roughness); float G_Vis = (G * VdotH) / (NdotH * NdotV); float Fc = pow(1.0 - VdotH, 5.0); A += (1.0 - Fc) * G_Vis; B += Fc * G_Vis; } } A /= float(SAMPLE_COUNT); B /= float(SAMPLE_COUNT); return vec2(A, B); } // ---------------------------------------------------------------------------- void main() { vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y); FragColor = integratedBRDF; }
Seperti yang Anda lihat, konvolusi BRDF diimplementasikan sebagai pengaturan yang hampir secara literal dari perhitungan matematika di atas. Parameter input kekasaran dan sudut diambil.ΞΈ , vektor sampel dibentuk berdasarkan sampel secara signifikan, diproses menggunakan fungsi geometri dan ekspresi Fresnel yang ditransformasikan untuk BRDF. Akibatnya, untuk setiap sampel, besarnya penskalaan dan perpindahan nilaiF 0 , yang pada akhirnya dirata-ratakan dan dikembalikan dalam bentukvec2. Dalampelajaranteoretis, disebutkan bahwa komponen geometris BRDF sedikit berbeda dalam hal penghitungan IBL, karena koefisienk ditentukan secara berbeda:k d i r e c t = ( Ξ± + 1 ) 28
k I B L = Ξ± 22
Karena konvolusi BRDF adalah bagian dari solusi integral dalam kasus penghitungan IBL, kami akan menggunakan koefisien k I B L untuk menghitung fungsi geometri dalam model Schlick-GGX: float GeometrySchlickGGX(float NdotV, float roughness) { float a = roughness; float k = (a * a) / 2.0; float nom = NdotV; float denom = NdotV * (1.0 - k) + k; return nom / denom; } // ---------------------------------------------------------------------------- float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; }
Harap perhatikan bahwa koefisien k dihitung berdasarkan parametera. Selain itu, dalam hal ini, parameterkekasarantidak dikuadratkan saat menggambarkan parametera, yang dilakukan di tempat lain di mana parameter ini diterapkan. Saya tidak yakin di mana masalahnya terletak: dalam karya Epic Games atau pada karya awal dari Disney, tetapi patut dikatakan bahwa penugasan langsung dari nilaikekasaran inipadaparametera yangmembuat peta integrasi BRDF identik, disajikan dalam publikasi Epic Games.Selanjutnya, hasil konvolusi BRDF akan disimpan dalam bentuk tekstur 2D ukuran 512x512: unsigned int brdfLUTTexture; glGenTextures(1, &brdfLUTTexture);
Seperti yang direkomendasikan oleh Epic Games, format tekstur floating-point 16-bit digunakan di sini. Pastikan untuk mengatur mode ulang ke GL_CLAMP_TO_EDGE untuk menghindari pengambilan sampel artefak dari tepi.Selanjutnya, kami menggunakan objek frame buffer yang sama dan mengeksekusi shader pada permukaan quad layar penuh: glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdfLUTTexture, 0); glViewport(0, 0, 512, 512); brdfShader.use(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); RenderQuad(); glBindFramebuffer(GL_FRAMEBUFFER, 0);
Hasilnya, kami mendapatkan peta tekstur yang menyimpan hasil konvolusi bagian ekspresi jumlah split yang bertanggung jawab untuk BRDF:Setelah mendapatkan hasil penyaringan awal dari peta lingkungan dan tekstur dengan hasil konvolusi BRDF, kita dapat mengembalikan hasil penghitungan integral untuk penerangan specular tidak langsung berdasarkan perkiraan dengan jumlah yang terpisah. Nilai yang dipulihkan kemudian akan digunakan sebagai radiasi specular tidak langsung atau latar belakang.Perhitungan Reflektansi Akhir dalam Model IBL
Jadi, untuk mendapatkan nilai yang menggambarkan komponen cermin tidak langsung dalam ekspresi reflektifitas umum, perlu untuk βmerekatkanβ komponen perkiraan yang dihitung menjadi satu keseluruhan sebagai jumlah yang terpisah. Pertama, tambahkan sampler yang sesuai ke shader akhir untuk data yang dihitung sebelumnya: uniform samplerCube prefilterMap; uniform sampler2D brdfLUT;
Pertama, kami mendapatkan nilai refleksi specular tidak langsung pada permukaan dengan pengambilan sampel dari peta lingkungan pra-diproses berdasarkan vektor refleksi. Harap dicatat bahwa di sini pemilihan level mip untuk pengambilan sampel didasarkan pada kekasaran permukaan. Untuk permukaan yang lebih kasar, pantulannya akan lebih buram : void main() { [...] vec3 R = reflect(-V, N); const float MAX_REFLECTION_LOD = 4.0; vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; [...] }
Pada tahap konvolusi awal, kami hanya menyiapkan 5 level mip (dari nol hingga keempat), MAX_REFLECTION_LOD konstan berfungsi untuk membatasi pemilihan dari level mip yang dihasilkan.Selanjutnya, kami membuat pilihan dari peta integrasi BRDF berdasarkan kekasaran dan sudut antara normal dan arah tampilan: vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y);
Nilai yang diperoleh dari peta berisi faktor penskalaan dan perpindahan untuk nilai tersebut F 0 (di sini kita ambil nilaiF- koefisien Fresnel). Nilai yang dikonversiFkemudian digabungkan dengan nilai yang diperoleh dari peta pra-penyaringan untuk mendapatkan solusi perkiraan ekspresi integral asli -specular.Dengan demikian, kami memperoleh solusi untuk bagian dari ekspresi reflektifitas, yang bertanggung jawab untuk refleksi specular. Untuk mendapatkan solusi lengkap dari model PBR IBL, Anda perlu menggabungkan nilai ini dengan solusi untuk bagian difus dari ekspresi reflektansi yang kami terima dalampelajaranterakhir: vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec3 kS = F; vec3 kD = 1.0 - kS; kD *= 1.0 - metallic; vec3 irradiance = texture(irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo; const float MAX_REFLECTION_LOD = 4.0; vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); vec3 ambient = (kD * diffuse + specular) * ao;
Saya perhatikan bahwa nilai specular tidak dikalikan dengan kS , karena sudah mengandung koefisien Fresnel.Mari kita jalankan aplikasi pengujian kami dengan satu set bola yang akrab dengan karakteristik perubahan sifat logam dan kekasaran dan lihat penampilan mereka dalam kemegahan penuh PBR:Anda dapat melangkah lebih jauh dan mengunduh satu set tekstur yang sesuai dengan model PBR dan mendapatkan bola dari bahan asli :Atau bahkan unduh model cantik bersama dengan tekstur PBR yang disiapkan dari Andrew Maximov :Saya pikir Anda tidak perlu meyakinkan siapa pun bahwa model pencahayaan saat ini terlihat jauh lebih meyakinkan. Dan terlebih lagi, pencahayaan terlihat benar secara fisik terlepas dari peta lingkungan. Di bawah ini digunakan beberapa peta lingkungan HDR yang benar-benar berbeda yang benar-benar mengubah sifat pencahayaan - tetapi semua gambar terlihat dapat diandalkan secara fisik, terlepas dari kenyataan bahwa Anda tidak harus menyesuaikan parameter apa pun dalam model! (Pada prinsipnya, penyederhanaan pekerjaan dengan material ini merupakan nilai tambah utama dari pipa PBR, dan gambaran yang lebih baik dapat dikatakan sebagai konsekuensi yang menyenangkan. Catatan. )Fuh, perjalanan kita ke esensi penyaji PBR cukup banyak. Kami sampai pada hasil melalui serangkaian langkah dan, tentu saja, banyak yang bisa salah dengan pendekatan pertama. Karena itu, untuk masalah apa pun, saya menyarankan Anda untuk berhati-hati memahami kode sampel untuk bidang monokrom dan bertekstur (dan kode shader, tentu saja!). Atau mintalah saran dalam komentar.Apa selanjutnya
Saya berharap bahwa dengan membaca baris-baris ini Anda telah mengembangkan pemahaman tentang karya model render PBR, serta menemukan dan berhasil meluncurkan aplikasi uji. Dalam pelajaran ini, kami menghitung semua peta tekstur tambahan yang diperlukan untuk model PBR dalam aplikasi kami sebelum siklus render utama. Untuk tugas pelatihan, pendekatan ini cocok, tetapi tidak untuk aplikasi praktis. Pertama, persiapan pendahuluan seperti itu harus dilakukan sekali, dan tidak setiap peluncuran aplikasi. Kedua, jika Anda memutuskan untuk menambahkan beberapa peta lingkungan lagi, maka Anda juga harus memprosesnya saat startup. Bagaimana jika beberapa kartu ditambahkan? Bola salju sungguhan.Itulah sebabnya, dalam kasus umum, peta iradiasi dan peta lingkungan pra-pemrosesan disiapkan sekali, dan kemudian disimpan ke disk (peta agregasi BRDF tidak tergantung pada peta lingkungan, sehingga umumnya dapat dihitung atau diunduh satu kali). Oleh karena itu, Anda akan memerlukan format untuk menyimpan kartu kubik HDR, termasuk level mipnya. Yah, atau Anda dapat menyimpan dan memuatnya menggunakan salah satu format yang banyak digunakan (jadi .dds mendukung penghematan level mip).Poin penting lainnya: untuk memberikan pemahaman yang mendalam tentang pipeline PBR dalam pelajaran ini, saya menggambarkan proses lengkap persiapan untuk render PBR, termasuk perhitungan awal kartu tambahan untuk IBL. Namun, dalam latihan Anda, Anda bisa menggunakan salah satu utilitas hebat yang menyiapkan kartu-kartu ini untuk Anda: misalnya cmftStudio atau IBLBaker .Kami juga tidak menganggap proses penyusunan peta kubus refleksi sampel ( probe refleksi) dan proses terkait interpolasi peta kubik dan koreksi paralaks. Secara singkat, teknik ini dapat dideskripsikan sebagai berikut: kami menempatkan dalam adegan kami banyak objek sampel refleksi, yang membentuk citra lingkungan lokal dalam bentuk peta kubik, dan kemudian semua peta tambahan yang diperlukan untuk model IBL dibentuk berdasarkan dasarnya. Dengan menginterpolasi data dari beberapa sampel berdasarkan jarak dari kamera, Anda bisa mendapatkan pencahayaan yang sangat rinci berdasarkan gambar, kualitas yang pada dasarnya dibatasi hanya oleh jumlah sampel yang siap kami tempatkan di tempat kejadian. Pendekatan ini memungkinkan Anda mengubah pencahayaan dengan benar, misalnya, ketika bergerak dari jalan yang terang ke senja kamar tertentu. Saya mungkin akan menulis pelajaran tentang tes refleksi di masa depan,Namun, saat ini, saya hanya dapat merekomendasikan artikel Chetan Jags di bawah ini untuk ditinjau.(Implementasi sampel, dan banyak lagi, dapat ditemukan di mesin mentah penulis tutorial di sini , kira-kira Per. )Bahan tambahan
- Bayangan Nyata di Mesin Unreal 4 : Penjelasan tentang pendekatan Epic Games untuk memperkirakan ekspresi untuk komponen cermin dengan jumlah terpisah. Berdasarkan artikel ini, kode untuk pelajaran IBL PBR ditulis.
- Pencahayaan Berbasis Fisik dan Pencahayaan Berbasis Gambar : Artikel bagus yang menggambarkan proses memasukkan perhitungan komponen cermin IBL dalam aplikasi pipa interaktif PBR.
- Pencahayaan Berbasis Gambar : Posting yang sangat panjang dan terperinci tentang IBL specular dan masalah terkait, termasuk masalah interpolasi probe cahaya.
- Moving Frostbite to PBR : , PBR Β«AAAΒ».
- Physically Based Rendering β Part Three : , IBL PBR JMonkeyEngine.
- Implementation Notes: Runtime Environment Map Filtering for Image Based Lighting : HDR , .