Ingin mempelajari cara menambahkan tekstur, pencahayaan, bayangan, peta normal, benda bercahaya, oklusi ambien, dan efek lainnya ke permainan 3D Anda? Hebat! Artikel ini menyajikan serangkaian teknik naungan yang dapat meningkatkan level grafik game Anda ke ketinggian baru. Saya menjelaskan setiap teknik sedemikian rupa sehingga Anda dapat menerapkan / port informasi ini pada tumpukan alat apa pun, baik itu Godot, Unity atau sesuatu yang lain.
Sebagai "perekat" antara shader, saya memutuskan untuk menggunakan mesin game Panda3D dan OpenGL Shading Language (GLSL) yang sangat baik. Jika Anda menggunakan tumpukan yang sama, Anda akan mendapatkan keuntungan tambahan - Anda akan belajar cara menggunakan teknik naungan khusus di Panda3D dan OpenGL.
Persiapan
Di bawah ini adalah sistem yang saya gunakan untuk mengembangkan dan menguji kode sampel.
Rabu
Kode sampel dikembangkan dan diuji dalam lingkungan berikut:
- Linux manjaro 4.9.135-1-MANJARO
- String penyaji OpenGL: GeForce GTX 970 / PCIe / SSE2
- String versi OpenGL: 4.6.0 NVIDIA 410.73
- g ++ (GCC) 8.2.1 20180831
- Panda3D 1.10.1-1
Material
Setiap bahan
Blender yang digunakan untuk membuat
mill-scene.egg
memiliki dua tekstur.
Tekstur pertama adalah peta normal, yang kedua adalah peta difus. Jika suatu objek menggunakan normals dari verteksnya, maka peta normal “biru polos” digunakan. Karena kenyataan bahwa semua model memiliki kartu yang sama di posisi yang sama, shader dapat digeneralisasi dan diterapkan pada simpul akar dari grafik adegan.
Perhatikan bahwa grafik adegan adalah
fitur dari implementasi mesin Panda3D.
Berikut ini adalah peta normal satu warna yang hanya berisi warna
[red = 128, green = 128, blue = 255]
.
Warna ini menunjukkan unit normal, menunjukkan ke arah positif dari sumbu z
[0, 0, 1]
.
[0, 0, 1] = [ round((0 * 0.5 + 0.5) * 255) , round((0 * 0.5 + 0.5) * 255) , round((1 * 0.5 + 0.5) * 255) ] = [128, 128, 255] = [ round(128 / 255 * 2 - 1) , round(128 / 255 * 2 - 1) , round(255 / 255 * 2 - 1) ] = [0, 0, 1]
Di sini kita melihat satuan normal
[0, 0, 1]
dikonversi menjadi warna biru polos
[128, 128, 255]
, dan biru pekat dikonversi menjadi satuan normal.
Ini dijelaskan secara lebih rinci di bagian teknik overlay peta normal.
Panda3d
Dalam contoh kode ini,
Panda3D digunakan sebagai "lem" antara shader. Ini tidak mempengaruhi teknik yang dijelaskan di bawah ini, yaitu, Anda dapat menggunakan informasi yang dipelajari di sini di tumpukan atau mesin permainan yang dipilih. Panda3D menyediakan fasilitas tertentu. Dalam artikel yang saya bicarakan, sehingga Anda dapat menemukan mitra mereka di tumpukan Anda, atau membuat ulang sendiri jika mereka tidak di tumpukan.
Perlu dipertimbangkan bahwa
gl-coordinate-system default
,
textures-power-2 down
dan
textures-auto-power-2 1
ditambahkan ke
config.prc
. Mereka tidak terkandung dalam
konfigurasi Panda3D standar.
Secara default, Panda3D menggunakan sistem koordinat tangan kanan dengan sumbu z ke atas, sedangkan OpenGL menggunakan sistem koordinat tangan kanan dengan sumbu y ke atas.
gl-coordinate-system default
memungkinkan Anda untuk menyingkirkan transformasi antara dua sistem koordinat di dalam shader.
textures-auto-power-2 1
memungkinkan kita untuk menggunakan ukuran tekstur yang bukan kekuatan dua, jika sistem mendukungnya.
Ini nyaman saat melakukan SSAO atau menerapkan teknik lain dalam layar / jendela, karena ukuran layar / jendela biasanya bukan kekuatan dua.
textures-power-2 down
mengurangi ukuran tekstur menjadi kekuatan dua jika sistem hanya mendukung tekstur dengan ukuran yang sama dengan kekuatan dua.
Buat Kode Contoh
Jika Anda ingin menjalankan kode sampel, Anda harus membuatnya terlebih dahulu.
Panda3D berjalan di Linux, Mac, dan Windows.
Linux
Mulailah dengan
menginstal Panda3D SDK untuk distribusi Anda.
Temukan di mana tajuk dan perpustakaan Panda3D berada. Kemungkinan besar, mereka berada di
/usr/include/panda3d/
dan di
/usr/lib/panda3d/
.
Kemudian klon repositori ini dan navigasikan ke direktori.
git clone https://github.com/lettier/3d-game-shaders-for-beginners.git
cd 3d-game-shaders-for-beginners
Sekarang kompilasi kode sumber menjadi file output.
g++ \
-c main.cxx \
-o 3d-game-shaders-for-beginners.o \
-std=gnu++11 \
-O2 \
-I/usr/include/python2.7/ \
-I/usr/include/panda3d/
Setelah membuat file output, buat file yang dapat dieksekusi dengan mengaitkan file output dengan dependensinya.
g++ \
3d-game-shaders-for-beginners.o \
-o 3d-game-shaders-for-beginners \
-L/usr/lib/panda3d \
-lp3framework \
-lpanda \
-lpandafx \
-lpandaexpress \
-lp3dtoolconfig \
-lp3dtool \
-lp3pystub \
-lp3direct \
-lpthread
Lihat
manual Panda3D untuk informasi lebih lanjut.
Mac
Mulai dengan menginstal
Panda3D SDK untuk Mac.
Temukan di mana tajuk dan perpustakaan Panda3D berada.
Kemudian klon repositori dan navigasikan ke direktori.
git clone https://github.com/lettier/3d-game-shaders-for-beginners.git
cd 3d-game-shaders-for-beginners
Sekarang kompilasi kode sumber menjadi file output. Anda perlu menemukan di mana direktori include berada di Python 2.7 dan Panda3D.
clang++ \
-c main.cxx \
-o 3d-game-shaders-for-beginners.o \
-std=gnu++11 \
-g \
-O2 \
-I/usr/include/python2.7/ \
-I/Developer/Panda3D/include/
Setelah membuat file output, buat file yang dapat dieksekusi dengan mengaitkan file output dengan dependensinya.
Anda perlu menemukan di mana perpustakaan Panda3D berada.
clang++ \
3d-game-shaders-for-beginners.o \
-o 3d-game-shaders-for-beginners \
-L/Developer/Panda3D/lib \
-lp3framework \
-lpanda \
-lpandafx \
-lpandaexpress \
-lp3dtoolconfig \
-lp3dtool \
-lp3pystub \
-lp3direct \
-lpthread
Lihat
manual Panda3D untuk informasi lebih lanjut.
Windows
Mulailah dengan
menginstal Panda3D SDK untuk Windows.
Temukan di mana tajuk dan perpustakaan Panda3D berada.
Kloning repositori ini dan navigasikan ke direktori.
git clone https://github.com/lettier/3d-game-shaders-for-beginners.git
cd 3d-game-shaders-for-beginners
Lihat
manual Panda3D untuk informasi lebih lanjut.
Luncurkan demo
Setelah membuat kode sampel, Anda dapat menjalankan file atau demo yang dapat dieksekusi. Ini adalah cara mereka berjalan di Linux atau Mac.
./3d-game-shaders-for-beginners
Dan mereka berjalan di Windows:
3d-game-shaders-for-beginners.exe
Kontrol keyboard
Demo ini memiliki kontrol keyboard yang memungkinkan Anda untuk memindahkan kamera dan mengubah status berbagai efek.
Gerakan
w
- pindah jauh ke tempat kejadian.a
- memutar adegan searah jarum jam.s
- menjauh dari tempat kejadian.d
- memutar adegan berlawanan arah jarum jam.
Efek yang bisa diubah
y
- aktifkan SSAO.Shift
+ y
- nonaktifkan SSAO.u
- dimasukkannya sirkuit.Shift
+ u
- nonaktifkan kontur.i
- aktifkan mekar.Shift
+ i
- nonaktifkan bloom.o
- aktifkan peta normal.Shift
+ o
- nonaktifkan peta normal.p
- masuknya kabut.Shift
+ p
- matikan kabut.h
- dimasukkannya kedalaman bidang.Shift
+ h
- nonaktifkan kedalaman bidang.j
- aktifkan posterisasi.Shift
+ j
- nonaktifkan posterisasik
- aktifkan pixelation.Shift
+ k
- nonaktifkan pixelisasi.l
- mengasah.Shift
+ l
- menonaktifkan ketajaman.n
dimasukkannya butiran film.Shift
+ n
- nonaktifkan butir film.
Sistem referensi
Sebelum Anda mulai menulis shader, Anda harus berkenalan dengan sistem referensi berikut atau mengoordinasikan sistem. Semuanya datang ke koordinat asal dari referensi saat ini diambil dari
(0, 0, 0)
. Segera setelah kami mengetahuinya, kami dapat mengubahnya menggunakan beberapa jenis matriks atau ruang vektor lainnya. Biasanya, jika output shader tidak terlihat benar, maka penyebabnya adalah sistem koordinat yang membingungkan.
Model
Sistem koordinat model atau objek relatif terhadap asal model. Dalam program pemodelan tiga dimensi, misalnya, dalam Blender, biasanya ditempatkan di tengah-tengah model.
Dunia
Ruang dunia relatif terhadap asal adegan / level / alam semesta yang Anda buat.
Ulasan
Ruang koordinat tampilan relatif terhadap posisi kamera aktif.
Kliping
Ruang kliping relatif ke tengah bingkai kamera. Semua koordinat di dalamnya homogen dan dalam interval
(-1, 1)
. X dan y sejajar dengan film kamera, dan koordinat z adalah kedalamannya.
Semua simpul yang tidak berada dalam batas piramida visibilitas atau volume visibilitas kamera terpotong atau dibuang. Kita melihat bagaimana ini terjadi dengan kubus terpotong di belakang oleh pesawat jauh dari kamera, dan dengan kubus yang terletak di samping.
Layar
Ruang layar (biasanya) relatif terhadap sudut kiri bawah layar. X berubah dari nol ke lebar layar. Y berubah dari nol ke tinggi layar.
GLSL
Alih-alih bekerja dengan pipeline fungsi tetap, kami akan menggunakan pipeline rendering GPU yang dapat diprogram. Karena dapat diprogram, kita sendiri harus memberikannya kode program dalam bentuk shader. Shader adalah program (biasanya kecil) yang dibuat dengan sintaks yang menyerupai bahasa C. Pipa rendering GPU yang dapat diprogram terdiri dari berbagai langkah yang dapat diprogram menggunakan shader. Berbagai jenis shader termasuk shader vertex, shading tessellation, geometris, fragmen, dan shader komputasi. Untuk menggunakan teknik yang dijelaskan dalam artikel, cukup bagi kita untuk menggunakan vertex dan fragmen
tahapan.
#version 140 void main() {}
Berikut adalah shader GLSL minimal, yang terdiri dari nomor versi GLSL dan fungsi utama.
#version 140 uniform mat4 p3d_ModelViewProjectionMatrix; in vec4 p3d_Vertex; void main() { gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; }
Berikut adalah vertex shader terpotong GLSL, yang mengubah input vertex menjadi ruang kliping dan menampilkan posisi baru ini sebagai posisi vertex yang seragam.
Prosedur
main
tidak mengembalikan apa-apa, karena itu
void
, dan variabel
gl_Position
adalah inline output.
Dua kata kunci yang layak disebutkan adalah:
uniform
dan
in
.
Kata kunci yang
uniform
berarti bahwa variabel global ini sama untuk semua simpul. Panda3D sendiri menetapkan
p3d_ModelViewProjectionMatrix
dan untuk setiap simpul itu adalah matriks yang sama.
Kata kunci
in
berarti bahwa variabel global ini diteruskan ke shader. Vertex shader mendapatkan setiap vertex yang terdiri dari geometri, tempat vertex shader terpasang.
#version 140 out vec4 fragColor; void main() { fragColor = vec4(0, 1, 0, 1); }
Berikut adalah shader fragmen GLSL yang dipangkas, menampilkan hijau buram sebagai warna fragmen.
Jangan lupa bahwa fragmen hanya memengaruhi satu piksel layar, tetapi beberapa fragmen dapat memengaruhi satu piksel.
Perhatikan kata kunci keluar.
Kata kunci
out
berarti bahwa variabel global ini ditetapkan oleh shader.
Nama
fragColor
opsional, sehingga Anda dapat memilih yang lain.
Ini adalah output dari dua shader yang ditunjukkan di atas.
Rendering tekstur
Alih-alih rendering / menggambar langsung di layar, kode sampel menggunakan teknik untuk
nama "render to tekstur" (render to tekstur). Untuk membuat tekstur, Anda perlu mengonfigurasi frame buffer dan mengikat tekstur ke sana. Anda dapat mengikat banyak tekstur ke buffer bingkai tunggal.
Tekstur yang diikat ke penyangga bingkai menyimpan vektor yang dikembalikan oleh fragmen shader. Biasanya vektor-vektor ini adalah vektor warna
(r, g, b, a)
, tetapi dapat berupa posisi atau vektor normal
(x, y, z, w)
. Untuk setiap tekstur yang diikat, shader fragmen dapat menampilkan vektor terpisah. Sebagai contoh, kita dapat menyimpulkan dalam satu posisi posisi dan normal dari titik.
Sebagian besar kode contoh yang berfungsi dengan Panda3D terkait dengan pengaturan
tekstur buffer bingkai . Untuk mempermudah, setiap fragmen shader dalam kode contoh hanya memiliki satu output. Namun, untuk memastikan frame rate tinggi (FPS), kita perlu menampilkan informasi sebanyak mungkin di setiap pass rendering.
Berikut adalah dua struktur tekstur untuk frame buffer dari kode sampel.
Struktur pertama menjadikan adegan watermill menjadi tekstur penyangga bingkai menggunakan berbagai vertex dan shader fragmen. Struktur ini melewati setiap simpul panggung dengan gilingan dan sepanjang fragmen yang sesuai.
Dalam struktur ini, kode contoh berfungsi sebagai berikut.
- Menyimpan data geometri (misalnya, posisi atau titik normal) untuk penggunaan di masa mendatang.
- Menyimpan data material (mis. Warna difus) untuk penggunaan di masa mendatang.
- Membuat pengikatan UV untuk berbagai tekstur (difus, peta normal, peta bayangan, dll.).
- Menghitung pencahayaan ambient, difus, tercermin, dan terpancar.
- Memberikan kabut.
Struktur kedua adalah kamera ortogonal yang ditujukan untuk persegi panjang dalam bentuk layar.
Struktur ini berjalan hanya melalui empat puncak dan fragmen yang sesuai.
Dalam struktur kedua, kode sampel melakukan tindakan berikut:
- Memproses output dari tekstur frame buffer lain.
- Menggabungkan tekstur penyangga bingkai yang berbeda menjadi satu.
Dalam contoh kode, kita bisa melihat output dari satu frame buffer tekstur, mengatur frame yang sesuai menjadi true, dan false ke yang lainnya.
Tekstur
Texturing adalah pengikatan warna atau vektor lain ke sebuah fragmen menggunakan koordinat UV. Nilai-nilai U dan V bervariasi dari nol hingga satu. Setiap vertex menerima koordinat UV dan ditampilkan dalam vertex shader.
Shader fragmen mendapat koordinat UV interpolasi. Interpolasi berarti bahwa koordinat UV untuk fragmen berada di suatu tempat antara koordinat UV dari simpul yang membentuk muka segitiga.
Vertex shader
#version 140 uniform mat4 p3d_ModelViewProjectionMatrix; in vec2 p3d_MultiTexCoord0; in vec4 p3d_Vertex; out vec2 texCoord; void main() { texCoord = p3d_MultiTexCoord0; gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; }
Di sini kita melihat bahwa shader puncak menghasilkan koordinat tekstur ke shader fragmen. Perhatikan bahwa ini adalah vektor dua dimensi: satu nilai untuk U dan satu untuk V.
Shader fragmen
#version 140 uniform sampler2D p3d_Texture0; in vec2 texCoord; out vec2 fragColor; void main() { texColor = texture(p3d_Texture0, texCoord); fragColor = texColor; }
Di sini kita melihat bahwa shader fragmen mencari warna dalam koordinat UV-nya dan menampilkannya sebagai warna fragmen.
Layar Isi Tekstur
#version 140 uniform sampler2D screenSizedTexture; out vec2 fragColor; void main() { vec2 texSize = textureSize(texture, 0).xy; vec2 texCoord = gl_FragCoord.xy / texSize; texColor = texture(screenSizedTexture, texCoord); fragColor = texColor; }
Saat rendering ke tekstur, mesh adalah persegi panjang datar dengan rasio aspek yang sama dengan layar. Karena itu, kita dapat menghitung koordinat UV, hanya mengetahui
A) lebar dan tinggi tekstur dengan ukuran layar ditumpangkan pada persegi panjang menggunakan koordinat UV, dan
B) koordinat x dan y dari fragmen.
Untuk mengikat x ke U, Anda harus membagi x dengan lebar tekstur yang masuk. Demikian pula, untuk mengikat y ke V, Anda harus membagi y dengan ketinggian tekstur yang masuk. Anda akan melihat bahwa teknik ini digunakan dalam kode sampel.
Pencahayaan
Untuk menentukan pencahayaan, perlu untuk menghitung dan menggabungkan aspek-aspek pencahayaan ambient, difus, tercermin dan dipancarkan. Kode sampel menggunakan pencahayaan Phong.
Vertex shader
Untuk setiap sumber cahaya, dengan pengecualian cahaya sekitar, Panda3D memberi kami struktur nyaman yang tersedia untuk vertex dan fragmen shader. Yang paling nyaman adalah peta bayangan dan matriks untuk melihat bayangan untuk mengubah simpul menjadi ruang bayangan atau pencahayaan.
Dimulai dengan vertex shader, kita harus mengubah dan menghapus vertex dari ruang pandang menjadi ruang bayangan atau pencahayaan untuk setiap sumber cahaya dalam adegan. Ini akan berguna di masa depan untuk shader fragmen untuk membuat bayangan. Ruang bayangan atau pencahayaan adalah ruang di mana setiap koordinat relatif terhadap posisi sumber cahaya (asal adalah sumber cahaya).
Shader fragmen
Shader fragmen melakukan sebagian besar perhitungan pencahayaan.
Material
Panda3D memberi kami materi (dalam bentuk struct) untuk mesh atau model yang saat ini kami render.
Berbagai Sumber Penerangan
Sebelum kita menjelajahi sumber-sumber iluminasi pemandangan, kita akan membuat drive yang akan berisi warna-warna yang tersebar dan dipantulkan.
Sekarang kita dapat berkeliling sumber cahaya dalam satu siklus, menghitung warna yang tersebar dan dipantulkan untuk masing-masingnya.
Vektor Terkait Pencahayaan
Berikut adalah empat vektor dasar yang diperlukan untuk menghitung warna difus dan pantulan yang diperkenalkan oleh setiap sumber cahaya. Vektor arah pencahayaan adalah panah biru yang menunjuk ke sumber cahaya. Vektor normal adalah panah hijau yang menunjuk ke atas secara vertikal. Vektor refleksi adalah panah biru yang mencerminkan vektor arah cahaya. Vektor mata atau pandangan adalah panah oranye yang menunjuk ke arah kamera.
Arah pencahayaan adalah vektor dari posisi titik ke posisi sumber cahaya.
Jika ini adalah pencahayaan terarah, maka Panda3D menetapkan
p3d_LightSource[i].position.w
nol. Pencahayaan terarah tidak memiliki posisi, hanya arah. Oleh karena itu, jika ini adalah pencahayaan terarah, maka arah iluminasi akan menjadi arah negatif atau berlawanan dengan sumber, karena untuk pencahayaan terarah Panda3D menetapkan
p3d_LightSource[i].position.xyz
ke
p3d_LightSource[i].position.xyz
.
Normal ke vertex harus berupa satuan vektor. Vektor satuan memiliki nilai yang sama dengan satu.
Selanjutnya, kita membutuhkan tiga vektor lagi.
Kita membutuhkan produk skalar dengan partisipasi arah pencahayaan, jadi lebih baik untuk menormalkannya. Ini memberi kita jarak atau besarnya sama dengan kesatuan (vektor satuan).
Arah pandangan berlawanan dengan posisi vertex / fragmen, karena posisi vertex / fragmen relatif terhadap posisi kamera. Jangan lupa bahwa posisi vertex / fragmen berada di ruang pandang. Oleh karena itu, alih-alih bergerak dari kamera (mata) ke vertex / fragmen, kami bergerak dari vertex / fragmen ke kamera (eye).
Vektor refleksi adalah refleksi dari arah pencahayaan normal ke permukaan. Ketika "sinar" cahaya menyentuh permukaan, itu dipantulkan pada sudut yang sama saat jatuh. Sudut antara vektor arah iluminasi dan normal disebut "sudut timbul." Sudut antara vektor refleksi dan normal disebut "sudut refleksi".
Anda perlu mengubah tanda vektor cahaya yang dipantulkan, karena harus mengarah ke arah yang sama dengan vektor mata. Jangan lupa bahwa arah mata beralih dari bagian atas / fragmen ke posisi kamera. Kami akan menggunakan vektor refleksi untuk menghitung kecerahan cahaya yang dipantulkan.
Pencahayaan baur
Kecerahan pencahayaan difus adalah produk skalar dari normal ke permukaan dan arah iluminasi vektor tunggal. Produk skalar dapat berkisar dari minus satu hingga satu. Jika kedua vektor menunjuk ke arah yang sama, maka kecerahannya adalah satu. Dalam semua kasus lain, itu akan kurang dari satu.
Jika vektor iluminasi mendekati arah yang sama seperti normal, maka kecerahan iluminasi difus cenderung menyatu.
Jika kecerahan pencahayaan difus kurang dari atau sama dengan nol, maka Anda harus pergi ke sumber cahaya berikutnya.
Sekarang kita dapat menghitung warna difus yang diperkenalkan oleh sumber ini.
Jika kecerahan pencahayaan difus sama dengan satu, maka warna difus akan menjadi campuran warna tekstur difus dan warna pencahayaan. Pada kecerahan lainnya, warna difus akan lebih gelap.Perhatikan bahwa saya membatasi warna difus agar tidak lebih terang daripada warna tekstur difus. Ini akan mencegah paparan berlebih dari adegan.Cahaya yang dipantulkan
Setelah pencahayaan difus, pantulan dihitung.
Kecerahan cahaya yang dipantulkan adalah produk skalar antara vektor mata dan vektor pantulan. Seperti dalam kasus kecerahan pencahayaan difus, jika dua vektor menunjuk ke arah yang sama, maka kecerahan pencahayaan yang dipantulkan sama dengan satu. Kecerahan lainnya akan mengurangi jumlah warna pantulan yang diperkenalkan oleh sumber cahaya ini.Kilau material menentukan berapa banyak cahaya yang dipantulkan akan tersebar. Biasanya diatur dalam program simulasi, misalnya di Blender. Dalam Blender, ini disebut kekerasan specular.Lampu sorot
Kode ini tidak memungkinkan pencahayaan memengaruhi fragmen di luar sorotan lampu sorot atau piramida. Untungnya, Panda3D dapat menentukan spotDirection
dan spotCosCutoff
bekerja dengan lampu terarah dan tempat. Lampu sorot memiliki posisi dan arah. Namun, pencahayaan terarah hanya memiliki arah, dan sumber titik hanya memiliki posisi. Namun, kode ini berfungsi untuk ketiga jenis pencahayaan tanpa perlu membingungkan jika pernyataan. spotCosCutoff = cosine(0.5 * spotlightLensFovAngle);
Jika dalam hal penerangan proyeksi produk skalar dari vektor "sumber fragmen penerangan" dan vektor arah proyektor kurang dari kosinus setengah sudut bidang pandangproyektor, maka shader tidak memperhitungkan pengaruh sumber ini.Perhatikan bahwa Anda harus mengubah tandanya unitLightDirection
. unitLightDirection
beralih dari fragmen ke lampu sorot, dan kita perlu pindah dari lampu sorot ke fragmen, karena ia spotDirection
langsung menuju pusat piramida lampu sorot pada jarak tertentu dari posisi lampu sorot.Dalam hal pencahayaan directional dan spot, Panda3D menetapkan spotCosCutoff
nilai ke -1. Ingat bahwa produk skalar bervariasi dalam kisaran dari -1 hingga 1. Oleh karena itu, tidak masalah apa yang akan terjadi unitLightDirectionDelta
, karena selalu lebih besar dari atau sama dengan -1.
Seperti kode unitLightDirectionDelta
, kode ini juga berfungsi untuk ketiga jenis sumber cahaya. Dalam kasus lampu sorot, itu akan membuat fragmen lebih terang saat mendekati pusat piramida sorotan. Untuk sumber cahaya arah dan titik spotExponent
adalah nol. Ingatlah bahwa nilai apa pun untuk kekuatan nol sama dengan kesatuan, sehingga warna difus sama dengan dirinya sendiri, dikalikan dengan satu, yaitu, tidak berubah.Bayangan
Panda3D menyederhanakan penggunaan bayangan karena ia menciptakan peta bayangan dan matriks transformasi bayangan untuk setiap sumber cahaya di tempat kejadian. Untuk membuat matriks transformasi sendiri, Anda perlu mengumpulkan matriks yang mengubah koordinat ruang tontonan menjadi ruang pencahayaan (koordinat relatif terhadap posisi sumber cahaya). Untuk membuat bayangan peta sendiri, Anda perlu membuat adegan dari sudut pandang sumber cahaya ke dalam tekstur penyangga bingkai. Tekstur buffer bingkai harus berisi jarak dari sumber cahaya ke fragmen. Ini disebut "peta kedalaman". Akhirnya, Anda perlu mentransfer secara manual ke shader peta kedalaman buatan sendiri uniform sampler2DShadow
, dan matriks transformasi bayangan uniform mat4
. Jadi kami akan menciptakan kembali apa yang Panda3D lakukan secara otomatis untuk kami.Cuplikan kode yang ditampilkan digunakan textureProj
, yang berbeda dari fungsi yang ditunjukkan di atas texture
. textureProj
membagi pertama vertexInShadowSpaces[i].xyz
dengan vertexInShadowSpaces[i].w
. Dia kemudian menggunakannya vertexInShadowSpaces[i].xy
untuk menemukan kedalaman yang tersimpan di peta bayangan. Kemudian dia menggunakan vertexInShadowSpaces[i].z
untuk membandingkan kedalaman bagian atas dengan kedalaman peta bayangan vertexInShadowSpaces[i].xy
. Jika perbandingan berhasil, itu textureProj
mengembalikan satu. Jika tidak, ia mengembalikan nol. Nol berarti bahwa simpul / fragmen ini ada dalam bayangan, dan satu berarti bahwa simpul / fragmen ini tidak ada dalam bayangan.Perhatikan bahwa textureProj
itu juga dapat mengembalikan nilai dari nol ke satu, tergantung pada bagaimana peta bayangan dikonfigurasi. Dalam contoh initextureProj
Melakukan beberapa tes kedalaman berdasarkan kedalaman yang berdekatan dan mengembalikan rata-rata tertimbang. Rata-rata tertimbang ini dapat memberikan kehalusan bayangan.Pelemahan
Jarak ke sumber cahaya hanyalah besarnya atau panjang vektor arah pencahayaan. Perhatikan bahwa kita tidak menggunakan arah penerangan yang dinormalisasi, karena jarak seperti itu akan sama dengan persatuan.Jarak ke sumber cahaya diperlukan untuk menghitung redaman. Atenuasi berarti bahwa efek cahaya dari sumber berkurang.Parameter constantAttenuation
, linearAttenuation
dan quadraticAttenuation
Anda dapat mengatur nilai apa pun. Layak dimulai dengan constantAttenuation = 1
, linearAttenuation = 0
dan quadraticAttenuation = 1
. Dengan parameter-parameter ini, pada posisi sumber cahaya itu sama dengan kesatuan dan cenderung nol ketika menjauh darinya.Pencahayaan warna terakhir
Untuk menghitung warna akhir pencahayaan, Anda perlu menambahkan warna difus dan pantulan. Penting untuk menambahkan ini ke drive dalam siklus melewati sumber cahaya di tempat kejadian.Ambient
Komponen pencahayaan ambient dalam model pencahayaan didasarkan pada warna ambien material, warna pencahayaan ambient, dan warna tekstur yang menyebar.Seharusnya tidak pernah ada lebih dari satu sumber cahaya sekitar, oleh karena itu perhitungan ini harus dilakukan hanya sekali, berbeda dengan perhitungan warna difus dan pantulan yang terakumulasi untuk setiap sumber cahaya.Harap dicatat bahwa warna cahaya sekitar sangat berguna saat melakukan SSAO.Menyatukan semuanya
Warna akhir adalah jumlah dari warna ambient, warna difus, warna pantulan, dan warna yang dipancarkan.Kode sumber
Peta Normal
Menggunakan peta normal memungkinkan Anda untuk menambahkan bagian baru ke permukaan tanpa geometri tambahan. Biasanya, ketika bekerja dalam program pemodelan 3D, dibuat versi mesh yang tinggi dan rendah. Kemudian normals dari simpul dari poly mesh tinggi diambil dan dibakar ke dalam tekstur. Tekstur ini adalah peta normal. Kemudian di dalam shader fragmen kami mengganti normals dari simpul-simpul dari mesh poli rendah dengan normals dari mesh poli tinggi yang dipanggang ke dalam peta normal. Karena ini, ketika menyalakan mesh, akan tampak bahwa ia memiliki lebih banyak poligon daripada yang sebenarnya. Ini memungkinkan Anda mempertahankan FPS tinggi, sambil mentransmisikan sebagian besar detail dari versi poli tinggi.Di sini kita melihat transisi dari model poli tinggi ke model poli rendah, dan kemudian ke model poli rendah dengan peta normal ditumpangkan.Namun, jangan lupa bahwa overlay peta normal hanyalah ilusi. Pada sudut tertentu, permukaan mulai terlihat datar lagi.Vertex shader
Dimulai dengan vertex shader, kita perlu menampilkan vektor normal, vektor binormal, dan vektor garis singgung ke fragmen shader. Vektor ini digunakan dalam shader fragmen untuk mengubah normal dari peta normal dari ruang singgung ke ruang tampilan.p3d_NormalMatrix
mengkonversi vektor normal dari vertex, binormal, dan vektor singgung ke ruang tampilan. Jangan lupa bahwa dalam ruang tampilan semua koordinat relatif terhadap posisi kamera.[p3d_NormalMatrix] adalah elemen transpose terbalik 3x3 teratas dari ModelViewMatrix. Struktur ini digunakan untuk mengubah vektor normal ke koordinat ruang tontonan.
Sumber
Kita juga perlu menampilkan koordinat UV dari peta normal ke shader fragmen.Shader fragmen
Ingat bahwa titik normal digunakan untuk menghitung pencahayaan. Namun, untuk menghitung pencahayaan, peta normal memberi kita normals lainnya. Dalam shader fragmen, kita perlu mengganti normals dari simpul dengan normals yang terletak di peta normal.
Menggunakan koordinat peta normal yang ditransfer oleh vertex shader, kami mengekstrak normal yang sesuai dari peta.
Di atas, saya menunjukkan bagaimana normals dikonversi ke warna untuk membuat peta normal. Sekarang kita perlu membalikkan proses ini sehingga kita bisa mendapatkan normals asli yang dipanggang ke peta. [ r, g, b] = [ r * 2 - 1, g * 2 - 1, b * 2 - 1] = [ x, y, z]
Beginilah proses pembongkaran normals dari peta normal.
Normal yang diperoleh dari peta normal biasanya dalam ruang singgung. Namun, mereka bisa berada di ruang lain. Misalnya, Blender memungkinkan Anda memanggang normalnya dalam ruang singgung, ruang objek, ruang dunia, dan ruang kamera.Untuk mentransfer normal peta normal dari ruang singgung ke ruang tampilan, buat matriks 3x3 berdasarkan vektor singgung, vektor binormal, dan vertex normal. Gandakan yang normal dengan matriks ini dan normalkan itu. Di sinilah kami berakhir dengan normals. Semua perhitungan pencahayaan lainnya masih dilakukan.Kode sumber