Pelajari OpenGL. Pelajaran 5.6 - Pemetaan Parallax

OGL3

Pemetaan paralaks


Teknik texturing Parallax Mapping agak mirip pengaruhnya terhadap Normal Mapping , tetapi didasarkan pada prinsip yang berbeda. Kesamaannya adalah bahwa, seperti Pemetaan Normal, teknik ini secara signifikan meningkatkan kompleksitas visual dan detail permukaan dengan tekstur yang diterapkan pada saat yang sama menciptakan ilusi yang masuk akal tentang adanya perbedaan ketinggian pada permukaan. Pemetaan Parallax bekerja sangat baik bersama dengan Pemetaan Normal untuk menciptakan hasil yang sangat andal: teknik yang dijelaskan menyampaikan efek bantuan yang jauh lebih baik daripada Pemetaan Normal, dan Pemetaan Normal melengkapinya untuk simulasi masuk akal pencahayaan dinamis. Pemetaan Parallax hampir tidak dapat dianggap sebagai teknik yang berhubungan langsung dengan metode simulasi pencahayaan, namun demikian saya memilih bagian ini untuk mempertimbangkannya, karena metode ini merupakan pengembangan logis dari ide-ide Pemetaan Normal. Saya juga mencatat bahwa untuk mengurai artikel ini, diperlukan pemahaman yang baik tentang algoritma Pemetaan Normal, terutama konsep ruang singgung atau ruang singgung .


Pemetaan Parallax milik keluarga Pemetaan Pemindahan atau teknik tekstur timbul yang mengimbangi simpul geometri berdasarkan nilai yang disimpan dalam peta tekstur khusus. Sebagai contoh, bayangkan sebuah pesawat terdiri dari urutan seribu simpul. Masing-masing dapat digeser sesuai dengan nilai yang dibaca dari tekstur, yang mewakili ketinggian pesawat pada titik tertentu. Tekstur seperti itu, yang mengandung nilai ketinggian di setiap texel, disebut peta ketinggian . Contoh peta semacam itu, yang diperoleh berdasarkan karakteristik geometris permukaan bata, adalah gambar berikut:


Dengan mengambil sampel dari peta ini dan menggeser setiap simpul sesuai dengan nilai ketinggian, dimungkinkan untuk mendapatkan permukaan cembung dari bidang ideal yang mengulangi parameter geometrik permukaan asli. Jadi, mengambil pesawat dengan jumlah simpul yang cukup dan menerapkan peta ketinggian dari contoh, Anda bisa mendapatkan hasil berikut:


Pendekatan yang dideskripsikan sederhana dan mudah diimplementasikan, tetapi membutuhkan kepadatan simpul yang tinggi dalam objek yang diproses, jika tidak, hasil dari pergeseran akan terlalu kasar. Dan jika pada setiap permukaan datar kita mulai melepaskan seribu atau lebih simpul, maka segera kita tidak akan punya waktu untuk membuat semua yang kita butuhkan. Mungkin ada algoritma yang memungkinkan Anda untuk mensimulasikan secara kualitatif kualitas dari algoritma Pemetaan Pemindahan naif, tetapi tanpa memerlukan biaya geometri seperti itu? Jika Anda berdiri, duduklah, karena pada gambar di atas sebenarnya hanya ada enam simpul (dua segitiga)! Relief batu bata ditiru dengan sempurna berkat penggunaan Parallax Mapping, sebuah teknik texturing lega yang tidak membutuhkan banyak simpul untuk dengan setia menyampaikan relief permukaan, tetapi, seperti Pemetaan Normal, yang menggunakan pendekatan asli untuk menipu mata pengamat.

Gagasan utama dari implementasi adalah untuk mendistorsi koordinat tekstur untuk fragmen saat ini berdasarkan arah pandangan dan data peta ketinggian sehingga dapat menciptakan ilusi bahwa fragmen ini milik bagian dari permukaan yang terletak lebih tinggi atau lebih rendah dari yang sebenarnya. Untuk pemahaman yang lebih baik tentang prinsip ini, lihat diagram contoh kita dengan batu bata:

Di sini, garis merah kasar mewakili nilai-nilai dari peta ketinggian, mencerminkan karakteristik geometris dari permukaan pasangan batu yang disimulasikan. Vektor  colororange barV mewakili arah dari permukaan ke pengamat ( viewDir ). Jika pesawat benar-benar timbul, maka pengamat akan melihat titik di permukaan  colorblueB . Namun, sebenarnya kita memiliki bidang yang ideal dan sinar ke arah pandangan melintasi bidang pada suatu titik  colorgreenA itu jelas. Tugas Pemetaan Parallax untuk menggeser koordinat tekstur pada suatu titik  colorgreenA sehingga mereka menjadi identik dengan koordinat yang sesuai dengan titik  colorblueB . Selanjutnya untuk fragmen saat ini (sesuai dengan titik  colorgreenA ) kami menggunakan koordinat titik yang diperoleh  colorblueB sampling tekstur diperlukan untuk semua, yang menciptakan ilusi bahwa pengamat melihat suatu hal  colorblueB .

Kesulitan utama terletak pada bagaimana cara menghitung koordinat tekstur suatu titik  colorblueB berada di titik  colorgreenA . Pemetaan Parallax menawarkan solusi perkiraan menggunakan penskalaan sederhana vektor arah dari permukaan  colororange barV untuk pengamat dengan ketinggian untuk fragmen  colorgreenA . Yaitu ubah saja panjangnya  colororange barV sehingga cocok dengan ukuran sampel dari peta ketinggian  colorgreenH(A) sesuai dengan fragmen  colorgreenA . Diagram di bawah ini menunjukkan hasil penskalaan - vektor  colorbrown barP :


Selanjutnya, vektor yang dihasilkan  colorbrown barP didekomposisi menjadi komponen sesuai dengan sistem koordinat pesawat itu sendiri, yang digunakan sebagai offset untuk koordinat tekstur asli. Apalagi sejak vektor  colorbrown barP dihitung menggunakan nilai dari peta tinggi, semakin tinggi nilai tinggi sesuai dengan fragmen saat ini, semakin kuat offsetnya.

Teknik sederhana ini memberikan hasil yang baik dalam beberapa kasus, tetapi masih merupakan perkiraan posisi titik yang sangat kasar  colorblueB . Jika peta ketinggian berisi area dengan nilai yang berubah tajam, maka hasil perpindahan menjadi salah: kemungkinan besar vektor  colorbrown barP bahkan menutup tidak akan jatuh ke sekitar titik  colorblueB :

Berdasarkan hal di atas, pertanyaan lain tetap: bagaimana menentukan cara memproyeksikan vektor dengan benar  colorbrown barP ke permukaan berorientasi sewenang-wenang untuk mendapatkan komponen untuk mengimbangi koordinat tekstur? Akan lebih baik untuk melakukan perhitungan dalam sistem koordinat tertentu, di mana perluasan vektor  colorbrown barP pada komponen x dan y akan selalu sesuai dengan dasar sistem koordinat tekstur. Jika Anda dengan hati-hati mengerjakan pelajaran tentang Pemetaan Normal , maka Anda sudah menebak bahwa kita berbicara tentang perhitungan dalam ruang singgung.

Mentransfer vektor dari permukaan ke pengamat  colororange barV ke ruang singgung kita mendapatkan vektor yang dimodifikasi  colorbrown barP , yang dekomposisi komponennya akan selalu dilakukan sesuai dengan vektor tangen dan bi-tangen untuk permukaan tertentu. Karena garis singgung dan garis singgung dua selalu selaras dengan sumbu sistem koordinat tekstur permukaan, maka, terlepas dari orientasi permukaan, Anda dapat dengan aman menggunakan komponen x dan y dari vektor.  colorbrown barP sebagai offset untuk koordinat tekstur.

Namun, teorinya cukup dan, setelah menyingsingkan lengan baju kami, kami beralih ke implementasi segera.

Pemetaan paralaks


Untuk implementasi, kita akan menggunakan bidang sederhana dengan garis singgung dan garis singgung bias yang dihitung untuknya - kita sudah dapat melakukan ini dari pelajaran tentang Pemetaan Normal. Kami menetapkan sejumlah bidang untuk bidang tekstur: difus , normal, dan offset , yang masing-masing tersedia di tautan yang sesuai. Dalam pelajaran, kita juga akan menggunakan Pemetaan Normal, karena Pemetaan Parallax menciptakan ilusi topografi permukaan, yang mudah rusak jika pencahayaan tidak berubah sesuai dengan topografi. Karena peta normal sering dibuat berdasarkan peta ketinggian, penggunaan gabungannya memastikan koneksi pencahayaan yang benar, dengan mempertimbangkan medan.

Anda mungkin sudah memperhatikan bahwa peta pemindahan yang ditunjukkan pada tautan di atas, pada kenyataannya, kebalikan dari peta yang diperlihatkan di awal pelajaran. Implementasi Pemetaan Parallax biasanya dilakukan hanya dengan menggunakan peta seperti itu, yang terbalik dengan peta ketinggian - peta kedalaman . Ini terjadi karena tiruan ceruk di pesawat agak lebih mudah daripada tiruan ketinggian. Sesuai dengan perubahan ini, skema kerja Pemetaan Parallax juga berubah:


Sekali lagi kita melihat titik-titik yang akrab  colorgreenA dan  colorblueB Namun, kali ini vektor  colorbrown barP diperoleh dengan mengurangi vektor  colororange barV dari koordinat tekstur pada suatu titik  colorgreenA . Kedalaman bukan ketinggian dapat diperoleh hanya dengan mengurangi sampel kedalaman dari persatuan atau membalikkan warna tekstur dalam editor gambar apa pun.

Pemetaan Parallax diimplementasikan dalam shader fragmen, karena data ketinggian berbeda untuk setiap fragmen di dalam segitiga. Kode shader fragmen akan membutuhkan perhitungan vektor dari fragmen ke pengamat  colororange barV , sehingga akan perlu untuk menyampaikan kepadanya posisi fragmen dan pengamat di ruang singgung. Menurut hasil pelajaran tentang Pemetaan Normal, kami masih memiliki vertex shader di tangan kami, yang mentransfer semua vektor ini sudah direduksi menjadi ruang singgung, kami akan menggunakannya:

#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; layout (location = 3) in vec3 aTangent; layout (location = 4) in vec3 aBitangent; out VS_OUT { vec3 FragPos; vec2 TexCoords; vec3 TangentLightPos; vec3 TangentViewPos; vec3 TangentFragPos; } vs_out; uniform mat4 projection; uniform mat4 view; uniform mat4 model; uniform vec3 lightPos; uniform vec3 viewPos; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0); vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); vs_out.TexCoords = aTexCoords; vec3 T = normalize(mat3(model) * aTangent); vec3 B = normalize(mat3(model) * aBitangent); vec3 N = normalize(mat3(model) * aNormal); mat3 TBN = transpose(mat3(T, B, N)); vs_out.TangentLightPos = TBN * lightPos; vs_out.TangentViewPos = TBN * viewPos; vs_out.TangentFragPos = TBN * vs_out.FragPos; } 

Dari hal-hal penting, saya hanya akan mencatat bahwa khusus untuk kebutuhan Pemetaan Parallax, perlu mentransfer aPos dan posisi pengamat viewPos di ruang singgung ke shader fragmen.

Di dalam shader, kami menerapkan algoritma Pemetaan Parallax, yang terlihat seperti ini:

 #version 330 core out vec4 FragColor; in VS_OUT { vec3 FragPos; vec2 TexCoords; vec3 TangentLightPos; vec3 TangentViewPos; vec3 TangentFragPos; } fs_in; uniform sampler2D diffuseMap; uniform sampler2D normalMap; uniform sampler2D depthMap; uniform float height_scale; vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir); void main() { vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos); //       Parallax Mapping vec2 texCoords = ParallaxMapping(fs_in.TexCoords, viewDir); //      //     vec3 diffuse = texture(diffuseMap, texCoords); vec3 normal = texture(normalMap, texCoords); normal = normalize(normal * 2.0 - 1.0); //  –     [...] } 

Kami mengumumkan fungsi ParallaxMapping, yang mengambil koordinat tekstur dari fragmen dan vektor dari fragmen ke pengamat  colororange barV di ruang singgung. Hasil dari fungsi ini adalah koordinat tekstur offset, yang sudah digunakan untuk sampel dari tekstur difus dan peta normal. Akibatnya, warna difus piksel dan normalnya sesuai dengan perubahan β€œgeometri” bidang.

Apa yang disembunyikan di dalam fungsi ParallaxMapping?

  vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) { float height = texture(depthMap, texCoords).r; vec2 p = viewDir.xy / viewDir.z * (height * height_scale); return texCoords - p; } 

Fungsi yang relatif sederhana ini adalah implementasi literal dari metode ini, poin-poin utama yang kita bahas di atas. Koordinat tekstur awal TexCoords diambil , dengan bantuan pemilihan ketinggian (atau kedalaman)  colorgreenH(A) dari depthMap untuk fragmen saat ini. Untuk menghitung vektor  colorbrown barP vektor viewDir diambil dalam ruang singgung dan pasangan komponen x dan y dibagi dengan komponen z , dan hasilnya diskalakan oleh nilai offset baca ketinggian . Seragam height_scale juga telah diperkenalkan untuk memberikan kontrol tambahan atas keparahan efek Pemetaan Parallax, karena biasanya efek offset terlalu kuat. Untuk mendapatkan hasilnya, kita kurangi vektor yang dihasilkan  colorbrown barP dari koordinat tekstur asli.

Kita akan berurusan dengan momen membagi viewDir.xy menjadi viewDir.z . Karena viewDir vektor dinormalisasi, komponennya z terletak pada interval [0, 1]. Ketika vektor hampir sejajar dengan permukaan komponen z mendekati nol, dan operasi pembagian mengembalikan vektor  colorbrown barP jauh lebih lama daripada jika viewDir dekat dengan tegak lurus ke permukaan. Dengan kata lain, kami skala vektor  colorbrown barP sehingga itu meningkat ketika Anda melihat permukaan pada sudut - ini memungkinkan Anda untuk mendapatkan hasil yang lebih realistis dalam kasus tersebut.

Beberapa pengembang memilih untuk menghapus penskalaan dengan membaginya dengan viewDir.z , karena, dalam kasus tertentu, pendekatan ini memberikan hasil yang salah ketika dilihat dari sudut pandang. Modifikasi teknik ini disebut Pemetaan Parallax dengan Offset Limiting . Pilihan opsi pendekatan, sebagian besar, tetap merupakan masalah preferensi pribadi - misalnya, saya lebih loyal terhadap hasil dari algoritma Pemetaan Parallax yang biasa.

Koordinat tekstur yang diubah pada akhirnya digunakan untuk memilih dari peta difus dan peta normal, yang memberi kita efek distorsi permukaan yang cukup bagus (parameter height_scale di sini dipilih mendekati 0,1):


Pada gambar, Anda dapat membandingkan efek dari Pemetaan Normal dan teknik Pemetaan Parallax. Karena Pemetaan Parallax mensimulasikan penyimpangan permukaan, situasi yang memungkinkan untuk teknik ini di mana batu bata bertumpang tindih, tergantung pada arah mata.

Artefak aneh juga terlihat di sepanjang batas bidang bertekstur. Mereka muncul karena fakta bahwa koordinat tekstur digeser oleh algoritma Pemetaan Parallax dapat berada di luar interval unit dan, tergantung pada mode pembungkus , menyebabkan hasil yang tidak diinginkan. Cara sederhana untuk menghilangkan artefak tersebut adalah dengan membuang semua fragmen yang koordinat teksturnya berada di luar interval satuan:

 texCoords = ParallaxMapping(fs_in.TexCoords, viewDir); if(texCoords.x > 1.0 || texCoords.y > 1.0 || texCoords.x < 0.0 || texCoords.y < 0.0) discard; 

Akibatnya, semua fragmen dengan koordinat tekstur bergeser yang keluar dari interval [0, 1] akan dibuang dan secara visual hasil tindakan Pemetaan Parallax akan menjadi dapat diterima. Jelas, metode penolakan ini tidak universal dan mungkin tidak berlaku untuk beberapa permukaan atau kasus texturing. Tetapi pada contoh pesawat, itu bekerja dengan sempurna dan membantu memperkuat efek mengubah relief pesawat:


Sumber sampel ada di sini .

Kelihatannya bagus, dan kinerja metode ini luar biasa - yang diperlukan hanyalah satu sampel tambahan dari tekstur! Tetapi kesederhanaan metode ini memiliki kelemahan yang signifikan: efek bantuan mudah dihancurkan ketika melihat pesawat pada sudut (yang juga berlaku untuk Pemetaan Normal) atau jika ada bagian di peta ketinggian dengan perubahan tajam dalam nilai:


Alasan penghancuran ilusi terletak pada kenyataan bahwa algoritme adalah perkiraan yang sangat kasar dari Pemetaan Pemindahan nyata. Namun, beberapa trik tambahan dapat membantu kami, yang memungkinkan kami untuk mendapatkan hasil yang hampir sempurna bahkan ketika melihat dari sudut atau saat menggunakan peta ketinggian dengan perubahan tajam. Misalnya, kita dapat menggunakan beberapa sampel dari peta ketinggian untuk menemukan titik terdekat dengan titik  colorblueB .

Pemetaan paralaks yang curam


Teknik Steep Parallax Mapping adalah pengembangan logis dari Pemetaan Parallax klasik: pendekatan yang sama digunakan dalam algoritma, tetapi alih-alih satu pilihan, beberapa diambil - untuk perkiraan vektor yang lebih baik  colorbrown barP digunakan untuk menghitung titik  colorblueB . Karena sampel tambahan ini, hasil algoritma secara visual jauh lebih masuk akal, bahkan ketika melihat sudut tajam ke permukaan.

Dasar dari pendekatan PM curam adalah untuk mengambil kisaran kedalaman tertentu dan membaginya menjadi lapisan berukuran sama. Selanjutnya, kita beralih melalui lapisan sambil secara bersamaan menggeser koordinat tekstur asli ke arah vektor  colorbrown barP dan membuat sampel dari peta kedalaman, berhenti pada saat ketika kedalaman dari sampel kurang dari kedalaman lapisan saat ini. Lihat diagram:


Seperti yang Anda lihat, kami bergerak melalui lapisan dari atas ke bawah dan untuk setiap lapisan kami membandingkan kedalamannya dengan nilai dari peta kedalaman. Jika kedalaman lapisan kurang dari nilai dari peta kedalaman, ini berarti vektor  colorbrown barP sesuai dengan lapisan ini terletak di atas permukaan. Proses ini diulangi sampai kedalaman lapisan lebih besar daripada pemilihan dari peta kedalaman: pada saat ini, vektor  colorbrown barP menunjuk ke titik di bawah topografi permukaan yang disimulasikan.
Contoh menunjukkan bahwa pemilihan dari peta kedalaman pada lapisan kedua ( D(2)=0,73 ) masih terletak "lebih dalam" sehubungan dengan kedalaman lapisan kedua sama dengan 0,4, yang berarti bahwa proses pencarian berlanjut. Dalam pass berikutnya, kedalaman lapisan 0,6 akhirnya ternyata "di atas" nilai sampel dari peta kedalaman ( D(3)=0,37 ) Dari sini kita dapat menyimpulkan bahwa vektor  colorbrown barP diperoleh untuk lapisan ketiga adalah posisi yang paling dapat diandalkan untuk geometri permukaan terdistorsi. Anda dapat menggunakan koordinat tekstur T3 berasal dari vektor  colorbrown barP , untuk mengimbangi koordinat tekstur dari fragmen saat ini. Jelas, keakuratan metode tumbuh dengan jumlah lapisan.

Perubahan dalam implementasi hanya akan memengaruhi fungsi ParallaxMapping, karena sudah berisi semua variabel yang diperlukan agar algoritma berfungsi:

 vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) { //    const float numLayers = 10; //    float layerDepth = 1.0 / numLayers; //    float currentLayerDepth = 0.0; //         //     P vec2 P = viewDir.xy * height_scale; vec2 deltaTexCoords = P / numLayers; [...] } 

Pertama, kita menginisialisasi: mengatur jumlah lapisan, menghitung kedalaman masing-masing, dan, akhirnya, menemukan ukuran perpindahan koordinat tekstur di sepanjang arah vektor  colorbrown barP , yang perlu digeser pada setiap layer.

Berikutnya adalah bagian melalui lapisan, mulai dari atas, sampai seleksi dari peta kedalaman ditemukan yang terletak "di atas" nilai kedalaman lapisan saat ini:

 //   vec2 currentTexCoords = texCoords; float currentDepthMapValue = texture(depthMap, currentTexCoords).r; while(currentLayerDepth < currentDepthMapValue) { //      P currentTexCoords -= deltaTexCoords; //          currentDepthMapValue = texture(depthMap, currentTexCoords).r; //     currentLayerDepth += layerDepth; } return currentTexCoords; 

Dalam kode ini, kami melintasi semua lapisan kedalaman dan menggeser koordinat tekstur asli hingga pemilihan dari peta kedalaman kurang dari kedalaman lapisan saat ini. Offset dilakukan dengan mengurangi dari koordinat tekstur asli delta berdasarkan vektor  colorbrown barP . Hasil algoritma adalah vektor offset dari koordinat tekstur, yang didefinisikan dengan akurasi jauh lebih besar daripada Pemetaan Parallax klasik.

Menggunakan sekitar 10 sampel, contoh bata menjadi tampak lebih realistis, bahkan jika dilihat dari sudut pandang. Namun yang terbaik dari semuanya, kelebihan dari Steep PM terlihat pada permukaan dengan peta kedalaman, yang memiliki perubahan tajam dalam nilai. Misalnya, seperti pada mainan kayu ini, sudah diperagakan sebelumnya:


Anda dapat meningkatkan algoritme sedikit lebih banyak jika Anda menganalisis sedikit fitur teknik Pemetaan Parallax. Jika Anda melihat permukaan kira-kira normal, maka tidak perlu menggeser koordinat tekstur dengan kuat, sementara ketika melihat suatu sudut, pergeseran cenderung ke maksimum (bayangkan secara mental arah pandang dalam kedua kasus). Jika Anda parameterkan jumlah sampel tergantung pada arah pandangan Anda, Anda dapat menyimpan cukup banyak di mana sampel tambahan tidak diperlukan:

 const float minLayers = 8.0; const float maxLayers = 32.0; float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir))); 

Hasil produk skalar dari viewDir vektor dan semi-sumbu positif Z digunakan untuk menentukan jumlah lapisan dalam interval [ minSamples , maxSamples ], yaitu arah pandangan menentukan jumlah iterasi efek yang diperlukan (dalam ruang bersinggungan, setengah sumbu positif Z diarahkan normal ke permukaan). Jika kita melihat sejajar dengan permukaan, efeknya akan menggunakan semua 32 lapisan.

Kode sumber yang dimodifikasi ada di sini . Saya juga mengusulkan untuk mengunduh tekstur mainan kayu: difus , peta normal , peta kedalaman .

Bukan tanpa pendekatan dan kelemahan. Karena jumlah sampel semuanya terbatas, penampilan efek aliasing tidak bisa dihindari, yang membuat transisi antar lapisan mencolok:


Anda dapat mengurangi tingkat keparahan artefak dengan meningkatkan jumlah sampel yang digunakan, tetapi cukup cepat memakan semua kinerja prosesor video yang tersedia. Ada beberapa tambahan pada metode, yang kembali sebagai akibat bukan titik pertama yang muncul di bawah permukaan imajiner, tetapi nilai yang diinterpolasi dari dua lapisan terdekat, yang memungkinkan kita untuk lebih memperjelas posisi titik tersebut.  colorblueB .

Dari metode ini, dua yang paling sering digunakan: Relief Parallax Mapping dan Parallax Occlusion Mapping , dengan Relief PM memberikan hasil yang paling dapat diandalkan, tetapi juga sedikit lebih menuntut kinerja dibandingkan dengan Pemetaan Parallax Occlusion Mapping. Karena Pemetaan Parallax Occlusion masih cukup dekat dengan kualitas untuk Relief PM dan pada saat yang sama bekerja lebih cepat, mereka lebih suka menggunakannya paling sering. Selanjutnya, implementasi Pemetaan Parallax Occlusion akan dipertimbangkan.

Pemetaan Oklusi Paralaks


Metode Pemetaan Oksidasi Parallax bekerja dengan prinsip dasar yang sama seperti Steep PM, tetapi alih-alih menggunakan koordinat tekstur dari lapisan pertama, di mana persimpangan dengan relief imajiner ditemukan, metode ini menggunakan interpolasi linier antara dua lapisan: lapisan setelah dan sebelum persimpangan. Koefisien pembobotan untuk interpolasi linier didasarkan pada rasio kedalaman relief saat ini dengan kedalaman kedua lapisan yang dipertimbangkan. Lihatlah diagram untuk mendapatkan pemahaman yang lebih baik tentang bagaimana semuanya bekerja:


Seperti yang Anda lihat, semuanya sangat mirip dengan Curam PM, hanya satu langkah tambahan interpolasi koordinat tekstur dari dua lapisan kedalaman yang berdekatan dengan titik persimpangan ditambahkan. Tentu saja, metode semacam itu hanyalah perkiraan, tetapi jauh lebih akurat daripada Curam PM.

Kode Pemetaan Oksidasi Parallax adalah tambahan untuk kode Curam PM dan tidak terlalu rumit:

 [...] // ,    Steep PM //       , // ..  " " vec2 prevTexCoords = currentTexCoords + deltaTexCoords; //         //      float afterDepth = currentDepthMapValue - currentLayerDepth; float beforeDepth = texture(depthMap, prevTexCoords).r - currentLayerDepth + layerDepth; //    float weight = afterDepth / (afterDepth - beforeDepth); vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); return finalTexCoords; 

Pada saat menemukan lapisan berbaring setelah titik persimpangan dengan relief imajiner, kami juga menentukan koordinat tekstur lapisan sebelum titik persimpangan. Selanjutnya, kami menemukan perpindahan kedalaman relief imajiner relatif terhadap kedalaman dua lapisan yang dipertimbangkan dan menggunakan rasio mereka sebagai koefisien bobot interpolasi linier lebih lanjut dari koordinat tekstur yang sesuai dengan dua lapisan yang dipertimbangkan. Hasil interpolasi dikembalikan oleh fungsi untuk digunakan di masa depan.

Parallax Occlusion Mapping memberikan hasil yang secara visual dapat diandalkan, meskipun dengan kekurangan kecil dan artefak aliasing. Tetapi untuk kompromi dalam kecepatan dan kualitas, mereka tidak signifikan dan hanya muncul dengan pengamatan dekat dari permukaan dekat dengan kamera atau pada sudut pandang yang sangat tajam.


Contoh kode ada di sini .

Pemetaan Parallax benar-benar merupakan teknik yang sangat baik yang memungkinkan Anda untuk secara dramatis meningkatkan detail visual adegan Anda, tetapi, tentu saja, memiliki kelemahan dalam bentuk artefak, yang patut diingat ketika menerapkan teknik dalam proyek. Untuk sebagian besar, Pemetaan Parallax digunakan pada permukaan datar seperti dinding atau lantai - di mana tidak begitu mudah untuk menentukan garis besar objek secara keseluruhan, dan sudut pandang permukaan sering dekat dengan tegak lurus. Dalam hal ini, cacat Pemetaan Parallax hampir tidak terlihat, dengan latar belakang peningkatan detail permukaan.

Bonus dari penerjemah:


Relief Parallax Mapping


Karena penulis menyebutkan dua metode untuk mengklarifikasi hasil dari curam PM, untuk kelengkapan, saya akan menjelaskan pendekatan kedua.

Seperti Pemetaan Parallax Occlusion, hasil dari eksekusi Steep PM digunakan di sini, mis. kita tahu kedalaman dua lapisan di antaranya terletak titik sebenarnya dari persimpangan vektor  colororange barV dengan lega, serta koordinat tekstur yang sesuai T2 dan T3 . Penyempurnaan estimasi titik persimpangan dalam metode ini adalah karena penggunaan pencarian biner.

Langkah-langkah algoritma penyempitan:

  • Lakukan perhitungan PM curam dan dapatkan koordinat tekstur T2 dan T3 - dalam interval ini terletak titik persimpangan vektor  c o l o r g r e e n b a r V  dengan topografi permukaan. Persimpangan sejati ditandai dengan palang merah.
  • Membagi menjadi dua nilai saat ini untuk offset koordinat tekstur dan ketinggian lapisan kedalaman.
  • Pindahkan koordinat tekstur dari titik T 3 dalam arah yang berlawanan dengan vektor  c o l o r g r e e n b a r V  oleh jumlah perpindahan. Kurangi kedalaman lapisan dengan nilai ukuran lapisan saat ini.
  • Pencarian biner langsung. Jumlah iterasi yang ditentukan diulang:
    1. Pilih dari peta kedalaman. Membagi offset tekstur saat ini dan ukuran lapisan kedalaman menjadi dua nilai saat ini.
    2. Jika ukuran sampel lebih besar dari kedalaman lapisan saat ini, maka tingkatkan kedalaman lapisan dengan ukuran lapisan saat ini, dan ubah koordinat tekstur sepanjang vektor  c o l o r g r e e n b a r V  ke offset saat ini.
    3. Jika ukuran sampel kurang dari kedalaman lapisan saat ini, maka kurangi kedalaman lapisan dengan ukuran lapisan saat ini, dan ubah koordinat tekstur di sepanjang vektor terbalik  c o l o r g r e e n b a r V  ke offset saat ini.

  • Koordinat tekstur terakhir yang diperoleh adalah hasil Relief PM.

Gambar menunjukkan bahwa setelah menemukan titik T 2 dan T 3 kita membagi dua ukuran layer dan ukuran offset dari koordinat tekstur, yang memberi kita titik iterasi pertama dari pencarian biner (1). Karena ukuran sampel di dalamnya ternyata lebih besar dari kedalaman lapisan saat ini, maka kami membagi dua parameter lagi dan bergerak bersama  c o l o r g r e e n b a r V  mendapatkan titik (2) dengan koordinat tekstur T hal yang akan menjadi hasil dari Curam PM untuk dua iterasi pencarian biner.

Kode Shader:

 //  : inTexCoords -   , // inViewDir -      - //  : lastDepthValue –      //      vec2 reliefPM(vec2 inTexCoords, vec3 inViewDir, out float lastDepthValue) { // ====== // ,   Steep PM // ====== const float _minLayers = 2.; const float _maxLayers = 32.; float _numLayers = mix(_maxLayers, _minLayers, abs(dot(vec3(0., 0., 1.), inViewDir))); float deltaDepth = 1./_numLayers; // uDepthScale –     PM vec2 deltaTexcoord = uDepthScale * inViewDir.xy/(inViewDir.z * _numLayers); vec2 currentTexCoords = inTexCoords; float currentLayerDepth = 0.; float currentDepthValue = depthValue(currentTexCoords); while (currentDepthValue > currentLayerDepth) { currentLayerDepth += deltaDepth; currentTexCoords -= deltaTexcoord; currentDepthValue = depthValue(currentTexCoords); } // ====== //   Relief PM // ====== //         deltaTexcoord *= 0.5; deltaDepth *= 0.5; //      ,   Steep PM currentTexCoords += deltaTexcoord; currentLayerDepth -= deltaDepth; //    … const int _reliefSteps = 5; int currentStep = _reliefSteps; while (currentStep > 0) { currentDepthValue = depthValue(currentTexCoords); deltaTexcoord *= 0.5; deltaDepth *= 0.5; //       , //       if (currentDepthValue > currentLayerDepth) { currentTexCoords -= deltaTexcoord; currentLayerDepth += deltaDepth; } //       else { currentTexCoords += deltaTexcoord; currentLayerDepth -= deltaDepth; } currentStep--; } lastDepthValue = currentDepthValue; return currentTexCoords; } 

Membayangi diri sendiri


Juga tambahan kecil tentang menambahkan naungan dari sumber cahaya yang dipilih ke algoritma perhitungan. Saya memutuskan untuk menambahkan, karena secara teknis metode perhitungannya identik dengan yang dipertimbangkan di atas, tetapi efeknya masih menarik dan menambah detail.

Faktanya, Curam PM yang sama diterapkan, tetapi pencarian tidak masuk jauh ke permukaan simulasi sepanjang garis pandang, tetapi dari permukaan, sepanjang vektor ke sumber cahayaˉ L .Vektor ini juga ditransfer ke ruang singgung dan digunakan untuk menentukan jumlah perpindahan koordinat tekstur. Pada output metode, koefisien penerangan material diperoleh dalam interval [0, 1], yang digunakan untuk memodulasi komponen difus dan cermin dalam perhitungan pencahayaan.
Untuk menentukan bayangan dengan tepi yang tajam, cukup berjalan di sepanjang vektorˉ L hingga titik terletak di bawah permukaan. Segera setelah titik tersebut ditemukan, kita mengambil koefisien iluminasi 0. Jika kita mencapai kedalaman nol tanpa memenuhi titik yang terletak di bawah permukaan, maka kita mengambil koefisien iluminasi sama dengan 1.Untuk menentukan naungan dengan tepi yang lembut, perlu untuk memeriksa beberapa titik yang terletak pada vektor

Λ‰ L dan terletak di bawah permukaan. Faktor naungan diambil sama dengan perbedaan antara kedalaman lapisan saat ini dan kedalaman dari peta kedalaman. Juga diperhitungkan adalah penghapusan titik berikutnya dari fragmen yang dimaksud dalam bentuk koefisien bobot yang sama dengan (1.0 - stepIndex / numberOfSteps). Pada setiap langkah, koefisien penerangan sebagian ditentukan sebagai:

P S F i = ( l a y e r H e i g h t i - h e i g h t F r o m t e x t u r e i ) βˆ— ( 1.0 - in u m S t e p s )


Hasil akhirnya adalah faktor cahaya maksimum dari semua parsial:

S F = m a x ( P S F i )


Skema metode:

Kemajuan metode untuk tiga iterasi dalam contoh ini:

  • Kami menginisialisasi total faktor cahaya menjadi nol.
  • Ambil langkah di sepanjang vektor Λ‰ L , langsung ke intinyaH a . Kedalaman titik jelas kurang dari pemilihan dari peta. H ( T L 1 ) - itu di bawah permukaan. Di sini kami melakukan pemeriksaan pertama dan, mengingat jumlah total cek, kami menemukan dan menyimpan faktor cahaya parsial pertama: (1.0 - 1.0 / 3.0).
  • Ambil langkah di sepanjang vektor Λ‰ L , langsung ke intinyaH b . Kedalaman titik jelas kurang dari pemilihan dari peta. H ( T L 2 ) - itu di bawah permukaan. Pemeriksaan kedua dan koefisien parsial kedua: (1.0 - 2.0 / 3.0).
  • Kami mengambil satu langkah lagi di sepanjang vektor dan mencapai kedalaman minimum 0. Hentikan gerakan.
  • Definisi hasil: jika tidak ada titik yang ditemukan di bawah permukaan, maka kami mengembalikan koefisien sama dengan 1 (tanpa naungan). Jika tidak, koefisien yang dihasilkan menjadi maksimum dari sebagian yang dihitung. Untuk digunakan dalam perhitungan pencahayaan, kami mengurangi nilai ini dari satu.

Contoh Kode Shader:

 //  : inTexCoords -   , // inLightDir -       - //  : inLastDepth –    , //      PM //       //    float getParallaxSelfShadow(vec2 inTexCoords, vec3 inLightDir, float inLastDepth) { float shadowMultiplier = 0.; //      , //    float alignFactor = dot(vec3(0., 0., 1.), inLightDir); if (alignFactor > 0.) { //   :  ,  //  ,     const float _minLayers = 16.; const float _maxLayers = 32.; float _numLayers = mix(_maxLayers, _minLayers, abs(alignFactor)); float _dDepth = inLastDepth/_numLayers; vec2 _dtex = uDepthScale * inLightDir.xy/(inLightDir.z * _numLayers); //  ,    int numSamplesUnderSurface = 0; //       //     L float currentLayerDepth = inLastDepth - _dDepth; vec2 currentTexCoords = inTexCoords + _dtex; float currentDepthValue = depthValue(currentTexCoords); //    float stepIndex = 1.; // ,       … while (currentLayerDepth > 0.) { //     ,     //       if (currentDepthValue < currentLayerDepth) { numSamplesUnderSurface++; float currentShadowMultiplier = (currentLayerDepth - currentDepthValue)*(1. - stepIndex/_numLayers); shadowMultiplier = max(shadowMultiplier, currentShadowMultiplier); } stepIndex++; currentLayerDepth -= _dDepth; currentTexCoords += _dtex; currentDepthValue = depthValue(currentTexCoords); } //      ,   //      1 if (numSamplesUnderSurface < 1) shadowMultiplier = 1.; else shadowMultiplier = 1. - shadowMultiplier; } return shadowMultiplier; } 

Koefisien yang dihasilkan digunakan untuk memodulasi hasil model pencahayaan Blinn-Fong yang digunakan dalam contoh:

 [...] //      vec3 fullColorADS = ambientTerm + attenuation * (diffuseTerm + specularTerm); //     -  fullColorADS *= pmShadowMultiplier; return fullColorADS; 

Perbandingan semua metode dalam satu kolase, volume 3MB.

Juga perbandingan video:


Bahan tambahan




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

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


All Articles