Malay (Melayu) translation by Meyria (you can also view the original English article)
Setelah menguasai asas-asas shader, kami mengambil pendekatan tangan untuk memanfaatkan kuasa GPU untuk mewujudkan pencahayaan dinamik yang dinamik.
Bahagian pertama siri ini meliputi asas-asas grafik shader. Bahagian kedua menerangkan prosedur am untuk menetapkan shaders untuk dijadikan rujukan untuk platform yang anda pilih. Dari sini di luar, kita akan menangani konsep umum pada pemayang grafik tanpa menganggap platform tertentu. (Untuk kemudahan, semua contoh kod akan tetap menggunakan JavaScript/WebGL.)
Sebelum pergi lagi, pastikan anda mempunyai cara untuk menjalankan shader yang anda selesa dengan. (JavaScript/WebGL mungkin paling mudah, tetapi saya menggalakkan anda untuk mencuba mengikuti di platform kegemaran anda!)
Matlamat
Pada akhir tutorial ini, anda bukan sahaja dapat membanggakan pemahaman yang mantap tentang sistem pencahayaan, tetapi anda akan membina sendiri dari awal.
Berikut adalah hasil akhir (klik untuk togol lampu):
Walaupun banyak enjin permainan menawarkan sistem pencahayaan yang sedia ada, memahami bagaimana ia dibuat dan bagaimana untuk mencipta anda sendiri memberikan anda lebih banyak fleksibiliti dalam mewujudkan rupa unik yang sesuai dengan permainan anda. Kesan Shader tidak semestinya kosmetik semata-mata, mereka boleh membuka pintu kepada mekanik permainan baru yang menarik!
Chroma adalah contoh yang hebat ini; watak pemain boleh berjalan di sepanjang bayang-bayang dinamik yang dibuat dalam masa nyata:
KBermula: Adegan Awal Kami
Kami akan melangkau banyak persediaan awal, kerana inilah tutorial sebelumnya yang dibuat secara eksklusif. Kami akan bermula dengan shader serpihan mudah yang memberikan tekstur kami:
Tiada apa yang terlalu mewah berlaku di sini. Kod JavaScript kami sedang menubuhkan adegan kami dan menghantar tekstur untuk menghasilkan, bersama dengan dimensi skrin kami, kepada shader.
var uniforms = { tex : {type:'t',value:texture},//The texture res : {type: 'v2',value:new THREE.Vector2(window.innerWidth,window.innerHeight)}//Keeps the resolution }
Dalam kod GLSL kami, kami mengisytiharkan dan menggunakan pakaian seragam ini:
uniform sampler2D tex; uniform vec2 res; void main() { vec2 pixel = gl_FragCoord.xy / res.xy; vec4 color = texture2D(tex,pixel); gl_FragColor = color; }
Kami pastikan anda menormalkan koordinat pixel kami sebelum menggunakannya untuk menarik tekstur.
Hanya untuk memastikan anda memahami segala-galanya yang berlaku di sini, inilah cabaran hangat:
Cabaran: Bolehkah anda membuat tekstur sambil mengekalkan nisbah aspek utuhnya? (Pergi sendiri dengan ini, kami akan berjalan melalui penyelesaian di bawah.)
Ia sepatutnya agak jelas mengapa ia diregangkan, tetapi di sini ada beberapa petunjuk: Lihat garis di mana kita menormalkan koordinat kami:
vec2 pixel = gl_FragCoord.xy / res.xy;
Kami membahagikan vec2
dengan vec2
, yang sama seperti membahagikan setiap komponen secara individu. Dalam erti kata lain, di atas bersamaan dengan:
vec2 pixel = vec2(0.0,0.0); pixel.x = gl_FragCoord.x / res.x; pixel.y = gl_FragCoord.y / res.y;
Kami membahagikan x dan y kami dengan nombor yang berlainan (lebar dan ketinggian skrin), jadi secara semula jadi akan diregangkan.
Apa yang akan berlaku jika kita membahagikan kedua-dua x dan y gl_FragCoord
hanya dengan x res
? Atau bagaimana pula dengan y?
Untuk kepentingan kesederhanaan, kami akan mengekalkan kod normal kami seperti-untuk tutorial lain, tetapi ia adalah baik untuk memahami apa yang berlaku di sini!
Langkah 1: Menambah Sumber Ringan
Sebelum kita boleh berbuat apa-apa, kita perlu mempunyai sumber cahaya. "Sumber cahaya" adalah tidak lebih dari satu titik yang kami hantar ke shader kami. Kami akan membina pakaian seragam baru untuk perkara ini:
var uniforms = { //Add our light variable here light: {type:'v3', value:new THREE.Vector3()}, tex : {type:'t',value:texture},//The texture res : {type: 'v2',value:new THREE.Vector2(window.innerWidth,window.innerHeight)}//Keeps the resolution }
Kami mencipta vektor dengan tiga dimensi kerana kami ingin menggunakan x
dan y
sebagai kedudukan cahaya pada skrin, dan z
sebagai radius.
Mari kita tetapkan beberapa nilai untuk sumber cahaya kami dalam JavaScript:
uniforms.light.value.z = 0.2;//Our radius
Kami berhasrat menggunakan jejari sebagai peratusan dimensi skrin, jadi 0.2
ialah 20% daripada skrin kami. (Tiada apa yang istimewa mengenai pilihan ini. Kita boleh menetapkan ini kepada saiz dalam piksel. Nombor ini tidak bermakna apa-apa sehingga kita melakukan sesuatu dengan kod GLSL kita.)
Untuk mendapatkan kedudukan tetikus dalam JavaScript, kami hanya menambah pendengar acara:
document.onmousemove = function(event){ //Update the light source to follow our mouse uniforms.light.value.x = event.clientX; uniforms.light.value.y = event.clientY; }
Sekarang mari kita tulis beberapa kod shader untuk menggunakan titik cahaya ini. Kami akan mulakan dengan tugas yang mudah: Kami mahu setiap piksel dalam julat cahaya kami dapat dilihat, dan segala-galanya harus menjadi hitam.
Menterjemahkannya ke dalam GLSL mungkin kelihatan seperti ini:
uniform sampler2D tex; uniform vec2 res; uniform vec3 light;//Remember to declare the uniform here! void main() { vec2 pixel = gl_FragCoord.xy / res.xy; vec4 color = texture2D(tex,pixel); //Distance of the current pixel from the light position float dist = distance(gl_FragCoord.xy,light.xy); if(light.z * res.x > dist){//Check if this pixel is without the range gl_FragColor = color; } else { gl_FragColor = vec4(0.0); } }
Apa yang telah kami lakukan di sini ialah:
- Mengisytiharkan pemboleh ubah seragam cahaya kami.
- Menggunakan fungsi jarak terbina dalam untuk mengira jarak antara kedudukan cahaya dan kedudukan piksel semasa.
- Semak jika jarak ini (dalam piksel) lebih besar daripada 20% lebar skrin; jika ya, kita kembali warna piksel itu, kalau tidak kita kembali hitam.
Uh oh! Sesuatu yang kelihatan dengan bagaimana cahaya mengikuti tetikus.
Cabaran: Bolehkah anda membetulkannya? (Sekali lagi, pergi sendiri sebelum kita berjalan di bawahnya.)
Memperbaiki Gerakan Cahaya
Anda mungkin ingat dari tutorial pertama dalam siri ini bahawa sumbu-y di sini dibalikkan. Anda mungkin tergoda untuk melakukan:
light.y = res.y - light.y;
Mana yang bunyi matematik, tetapi jika anda melakukannya, shader anda tidak akan dikompilasi! Masalahnya ialah pemboleh ubah seragam tidak boleh diubah. Untuk melihat mengapa, ingat bahawa kod ini berjalan untuk setiap piksel tunggal selari. Bayangkan semua teras prosesor yang cuba mengubah pemboleh ubah tunggal pada masa yang sama. Tidak baik!
Kita boleh membetulkannya dengan membuat pemboleh ubah baru dan bukannya cuba menyunting pakaian seragam kita. Atau lebih baik lagi, kita boleh melakukan langkah ini sebelum menyerahkannya kepada shader:
uniforms.light.value.y = window.innerHeight - event.clientY;
Kami sekarang telah berjaya menentukan julat pemandangan kami yang kelihatan. Ia kelihatan sangat tajam, walaupun ....
Menambah Kecerunan
Daripada sekadar memotong ke hitam apabila kita berada di luar jangkauan, kita boleh mencuba kecerunan lancar ke arah tepi. Kita boleh melakukan ini dengan menggunakan jarak yang kita sudah dikira.
Daripada menetapkan semua piksel di dalam julat yang kelihatan kepada warna tekstur, seperti:
gl_FragColor = color;
Kita boleh membiak bahawa dengan faktor jarak:
gl_FragColor = color * (1.0 - dist/(light.z * res.x));
Ini berfungsi kerana dist
ialah jarak dalam piksel antara piksel semasa dan sumber cahaya. Istilah (light.z * res.x)
adalah panjang jari-jari. Oleh itu, apabila kita melihat pixel tepat pada sumber cahaya, dist
adalah 0
, jadi kita akhirnya mendarabkan warna
dengan 1
, iaitu warna penuh.

, dist
dikira untuk beberapa piksel sewenang-wenangnya. dist
adalah berbeza bergantung pada pixel yang kita berada, sedangkan light.z * res.x
adalah malar.Apabila kita melihat piksel di pinggir bulatan, dist
adalah sama dengan panjang jejari, jadi kita akhirnya mendarabkan warna
dengan 0
, yang berwarna hitam.
Langkah 2: Menambah Kedalaman
Setakat ini kami tidak melakukan lebih banyak daripada membuat topeng gradien untuk tekstur kami. Semuanya masih terlihat datar. Untuk memahami cara memperbaikinya, mari kita lihat apa yang sedang dilakukan sistem pencahayaan kita saat ini, dibandingkan dengan apa yang sepatutnya dilakukan.

Dalam senario di atas, anda akan mengharapkan A menjadi yang paling dinyalakan, memandangkan sumber cahaya kami dihidupkan secara langsung, dengan B dan C menjadi gelap, kerana hampir tiada sinaran cahaya sebenarnya memukul pihak.
Walau bagaimanapun, inilah yang kita lihat sistem cahaya sekarang:

Mereka semua diperlakukan sama, kerana satu-satunya faktor yang kita ambil perhatian adalah jarak pada pesawat xy. Sekarang, anda mungkin berfikir bahawa semua yang kita perlukan sekarang adalah ketinggian setiap mata, tetapi itu tidak betul. Untuk melihat mengapa, pertimbangkan senario ini:

A ialah bahagian atas blok kami, dan B dan C adalah sisinya. D adalah satu lagi tanah yang berdekatan. Kita dapat melihat bahawa A dan D sepatutnya paling terang, dengan D menjadi sedikit lebih gelap kerana lampu itu mencapai sudut. B dan C, sebaliknya, harus sangat gelap, kerana hampir tidak ada cahaya yang menjangkau mereka, kerana mereka menghadapi jauh dari sumber cahaya.
Ia bukan ketinggian sehinggalah arah yang menghadap ke permukaan yang kita perlukan. Ini dikenali sebagai permukaan biasa.
Tetapi bagaimana kita menyampaikan maklumat ini kepada shader? Kita tidak boleh menghantar ribuan nombor raksasa untuk setiap pixel tunggal, bolehkah kita? Sebenarnya, kita sudah berbuat demikian! Kecuali kita tidak menyebutnya array, kita menyebutnya tekstur.
Inilah peta biasa; ia hanya imej di mana nilai r
, g
dan b
setiap piksel mewakili arah dan bukan warna.

Di atas adalah peta biasa yang biasa. Jika kita menggunakan pemetik warna, kita dapat melihat bahawa lalai, arah "rata" diwakili oleh warna (0.5, 0.5, 1)
(warna biru yang mengambil majoriti imej). Inilah arah yang menunjuk ke arah lurus. Nilai x, y dan z dipetakan ke nilai r, g dan b.
Sisi miring di sebelah kanan menunjuk ke kanan, jadi nilai xnya lebih tinggi; nilai x juga nilai merah, itulah sebabnya ia kelihatan lebih merah/merah jambu. Perkara yang sama berlaku untuk semua pihak yang lain.
Ia kelihatan lucu kerana ia tidak dimaksudkan untuk diberikan; ia dibuat semata-mata untuk mengekod nilai-nilai normal permukaan ini.
Oleh itu, mari kita memuatkan peta normal ini untuk menguji dengan:
var normalURL = "https://raw.githubusercontent.com/tutsplus/Beginners-Guide-to-Shaders/master/Part3/normal_maps/normal_test.jpg" var normal = THREE.ImageUtils.loadTexture(normalURL);
Dan tambahnya sebagai salah satu pemboleh ubah seragam kami:
var uniforms = { norm: {type:'t', value:normal}, //.. the rest of our stuff here }
Untuk menguji bahawa kami telah memuatkannya dengan betul, mari kita cuba menjadikannya bukan tekstur kita dengan menyunting kod GLSL kami (ingat, kami menggunakannya sebagai tekstur latar belakang, dan bukannya peta normal pada ketika ini):
Langkah Tiga: Menggunakan Model Lampu
Sekarang kita mempunyai data biasa permukaan kita, kita perlu melaksanakan model pencahayaan. Dalam erti kata lain, kita perlu memberitahu permukaan kita bagaimana untuk mengambil kira semua faktor yang kita perlu untuk mengira kecerahan akhir.
Model Phong adalah yang paling mudah yang kita boleh laksanakan. Begini bagaimana ia berfungsi: Memandangkan permukaan dengan data normal seperti ini:

Kami hanya mengira sudut antara sumber cahaya dan permukaan biasa:

Lebih kecil sudut ini, lebih cerah piksel.
Ini bermakna bahawa piksel langsung di bawah sumber cahaya, di mana perbezaan sudut adalah 0, akan menjadi paling terang. Piksel paling gelap ialah mereka yang menunjukkan arah yang sama dengan sinar cahaya (yang akan menjadi seperti bahagian bawah objek)
Sekarang mari kita laksanakan ini.
Oleh kerana kami menggunakan peta normal yang mudah untuk diuji, mari kita tetapkan tekstur kami kepada warna pepejal supaya kami dapat dengan mudah mengetahui sama ada ia berfungsi.
Jadi, bukannya:
vec4 color = texture2D(...);
Mari kita buatnya putih padat (atau mana-mana warna yang anda suka):
vec4 color = vec4(1.0); //solid white
Ini adalah GLSL untuk membuat vec4
dengan semua komponen bersamaan dengan 1.0
.
Inilah yang kelihatan seperti algoritma kami:
- Dapatkan vektor normal pada piksel ini.
- Dapatkan vektor arah cahaya.
- Biasakan vektor kita.
- Hitung sudut di antara mereka.
- Keluarkan warna terakhir dengan faktor ini.
1. Dapatkan Vektor Normal di Piksel Ini
Kita perlu tahu arah mana permukaan yang dihadapi supaya kita dapat mengira berapa banyak cahaya yang seharusnya mencapai pixel ini. Arah ini disimpan dalam peta normal kami, jadi mendapatkan vektor normal kami hanya bermaksud mendapatkan warna piksel semasa tekstur biasa:
vec3 NormalVector = texture2D(norm,pixel).xyz;
Oleh kerana nilai alfa tidak mewakili apa-apa dalam peta normal, kita hanya memerlukan tiga komponen pertama.
2. Dapatkan Vektor Arah Cahaya
Sekarang kita perlu tahu di mana arah cahaya kita menunjuk. Kita boleh bayangkan permukaan cahaya kita adalah lampu suluh yang dipegang di hadapan skrin, di lokasi tetikus kita, jadi kita boleh mengira vektor arah cahaya dengan hanya menggunakan jarak antara sumber cahaya dan piksel:
vec3 LightVector = vec3(light.x - gl_FragCoord.x,light.y - gl_FragCoord.y,60.0);
Ia perlu mempunyai koordinat z juga (untuk dapat mengira sudut terhadap vektor normal permukaan 3-dimensi). Anda boleh bermain-main dengan nilai ini. Anda akan mendapati bahawa lebih kecil itu, kontras yang lebih tajam adalah antara kawasan terang dan gelap. Anda boleh memikirkan ini sebagai ketinggian yang anda memegang lampu suluh anda di atas tempat kejadian; semakin jauh, cahaya yang lebih teragih diagihkan.
3. Menetapkan Normal Vektor kami
Kini untuk menormalkan:
NormalVector = normalize(NormalVector); LightVector = normalize(LightVector);
Kami menggunakan fungsi terbina dalam untuk menormalkan untuk memastikan kedua vektor kami mempunyai panjang 1.0
. Kita perlu melakukan ini kerana kita akan menghitung sudut menggunakan produk dot. Sekiranya anda sedikit kabur mengenai cara kerja ini, anda mungkin mahu menyusun beberapa algebra linear anda. Untuk keperluan kami, anda hanya perlu tahu bahawa produk titik akan mengembalikan kosina sudut antara dua vektor sama panjangnya.
4. Kira Sudut Antara Vektor Kami
Mari maju dan lakukannya dengan fungsi titik terbina dalam:
float diffuse = dot( NormalVector, LightVector );
Saya panggil ia meresap hanya kerana ini adalah istilah ini yang dipanggil dalam model pencahayaan Phong, kerana bagaimana ia menentukan berapa banyak cahaya yang mencapai permukaan adegan kita.
5. Melipatgandakan Warna Akhir oleh Faktor Ini
Itu sahaja! Sekarang, teruskan dan tambahkan warna anda dengan istilah ini. Saya pergi ke hadapan dan mencipta pembolehubah yang dikenali sebagai distanceFactor
supaya persamaan kita kelihatan lebih mudah dibaca:
float distanceFactor = (1.0 - dist/(light.z * res.x)); gl_FragColor = color * diffuse * distanceFactor;
Dan kami mendapat model lampu kerja! (Anda mungkin mahu mengembangkan radius cahaya anda untuk melihat kesannya lebih jelas.)
Hmm, sesuatu seolah-olah sedikit. Rasanya seperti cahaya kita dimiringkan entah bagaimana.
Mari kita menyemak semula matematik kita selama ini. Kami mempunyai vektor cahaya ini:
vec3 LightVector = vec3(light.x - gl_FragCoord.x,light.y - gl_FragCoord.y,60.0);
Yang kita tahu akan memberi kita (0, 0, 60)
apabila cahaya berada di atas pixel ini. Selepas kita menormalkannya, ia akan menjadi (0, 0, 1)
.
Ingatlah bahawa kita mahu normal yang menunjuk terus ke arah cahaya untuk mempunyai kecerahan maksimum. Permukaan lalai kita biasa, menunjuk ke atas, adalah (0.5, 0.5, 1)
.
Cabaran: Bolehkah anda melihat penyelesaian sekarang? Bolehkah anda melaksanakannya?
Masalahnya adalah bahawa anda tidak boleh menyimpan nombor negatif sebagai nilai warna dalam tekstur. Anda tidak boleh menandakan vektor yang menunjuk ke kiri sebagai (-0.5, 0, 0)
. Jadi, orang yang membuat peta normal perlu menambah 0.5
kepada semua. (Atau, dalam istilah yang lebih umum, mereka perlu mengalihkan sistem koordinat mereka). Anda perlu sedar tentang ini untuk mengetahui bahawa anda harus menolak 0.5
dari setiap piksel sebelum menggunakan peta.
Inilah yang kelihatan seperti demo selepas menolak 0.5
dari x dan y vektor normal kami:
Terdapat satu pembetulan terakhir yang perlu kita buat. Ingat bahawa produk titik mengembalikan kosinus sudut. Ini bermakna output kami diapit antara -1 dan 1. Kami tidak mahu nilai negatif dalam warna kami, dan sementara WebGL nampaknya secara automatik membuang nilai-nilai negatif ini, anda mungkin mendapat tingkah laku pelik di tempat lain. Kita boleh menggunakan fungsi max terbina dalam untuk menyelesaikan masalah ini, dengan mengubahnya:
float diffuse = dot( NormalVector, LightVector );
Ke dalam ini:
float diffuse = max(dot( NormalVector, LightVector ),0.0);
Sekarang anda mempunyai model lampu kerja!
Anda boleh meletakkan tekstur batu, dan anda boleh mencari peta sebenar dalam repo GitHub untuk siri ini (atau, secara langsung, di sini):
Kami hanya perlu menukar satu baris JavaScript, dari:
var normalURL = "https://raw.githubusercontent.com/tutsplus/Beginners-Guide-to-Shaders/master/Part3/normal_maps/normal_test.jpg"
Untuk
var normalURL = "https://raw.githubusercontent.com/tutsplus/Beginners-Guide-to-Shaders/master/Part3/normal_maps/blocks_normal.JPG"
Dan satu garisan GLSL, dari:
vec4 color = vec4(1.0);//solid white
Dan anda akan melihat beberapa kacang jeli yang lazat, merentang di skrin kami:
vec4 color = texture2D(tex,pixel);
Dan inilah hasil akhir:
Tips Pengoptimuman
GPU sangat berkesan dalam apa yang ia lakukan, tetapi mengetahui apa yang boleh memperlahankannya adalah sangat berharga. Berikut adalah beberapa petua mengenai perkara itu:
Cabang
Satu perkara tentang shaders adalah bahawa ia umumnya lebih baik untuk mengelakkan cawangan apabila mungkin. Walaupun anda jarang perlu bimbang tentang sekumpulan if
pernyataan pada mana-mana kod yang anda tulis untuk CPU, mereka boleh menjadi hambatan utama bagi GPU.
Untuk mengetahui mengapa, ingat lagi bahawa kod GLSL anda berjalan pada setiap piksel pada skrin selari. Kad grafik boleh membuat banyak pengoptimuman berdasarkan fakta bahawa semua piksel perlu menjalankan operasi yang sama. Sekiranya terdapat sekumpulan if
kenyataan, bagaimanapun, beberapa pengoptimuman mungkin akan gagal, kerana piksel yang berbeza akan menjalankan kod yang berbeza sekarang. Sama ada atau tidak if
kenyataan benar-benar memperlahankan sesuatu, ia bergantung kepada pelaksanaan hardware dan graphics card tertentu, tetapi ia adalah perkara yang baik untuk diingat ketika cuba mempercepatkan shader anda.
Renderan Tertunda
Ini adalah konsep yang sangat berguna apabila berurusan dengan pencahayaan. Bayangkan jika kita mahu mempunyai dua sumber cahaya, atau tiga, atau sedozen; kita perlu mengira sudut antara setiap permukaan yang normal dan setiap titik cahaya. Ini akan cepat memperlahankan shader kami untuk merangkak. Renderan ditunda adalah satu cara untuk mengoptimumkan bahawa dengan memisahkan kerja shader kami menjadi berbilang pas. Berikut adalah artikel yang masuk ke butiran mengenai apa yang dimaksudkan. Saya akan memetik bahagian yang relevan untuk tujuan kami di sini:
Pencahayaan adalah sebab utama untuk pergi satu laluan berbanding yang lain. Dalam saluran paip penyampaian yang standard, pengiraan pencahayaan perlu dilakukan pada setiap puncak dan pada setiap serpihan dalam adegan yang kelihatan, untuk setiap cahaya di tempat kejadian.
Sebagai contoh, bukannya menghantar pelbagai titik cahaya, anda mungkin akan menarik mereka semua ke tekstur, sebagai lingkaran, dengan warna pada setiap piksel yang mewakili keamatan cahaya. Dengan cara ini, anda akan dapat mengira kesan gabungan semua lampu di tempat kejadian anda, dan hanya menghantar tekstur akhir (atau penampan seperti yang kadang-kadang dipanggil) untuk mengira pencahayaan dari.
Pembelajaran untuk memecah kerja ke dalam pelbagai pas untuk shader adalah teknik yang sangat berguna. Kesan blur menggunakan idea ini untuk mempercepatkan shader, contohnya, serta kesan seperti shader/shader shader. Ia keluar dari skop tutorial ini, tetapi kita mungkin akan kembali ke teknik dalam tutorial masa depan!
Langkah Seterusnya
Sekarang bahawa anda mempunyai shader pencahayaan yang bekerja, berikut adalah beberapa perkara yang perlu dicuba dan dimainkan:
- Cuba bervariasi ketinggian (nilai
z
) vektor cahaya untuk melihat kesannya - Cuba bervariasi keamatan cahaya. (Anda boleh melakukannya dengan mendarabkan istilah meresap anda dengan faktor.)
- Tambah istilah ambien kepada persamaan cahaya anda. (Ini pada dasarnya bermakna memberi nilai minimum, supaya kawasan gelap tidak akan menjadi hitam. Ini membantu menjadikannya lebih realistik kerana perkara-perkara dalam kehidupan sebenar masih diterangi walaupun tidak ada cahaya langsung yang memukul mereka)
- Cuba laksanakan beberapa shader dalam tutorial WebGL ini. Ia dilakukan dengan Babylon.js bukannya Three.js, tetapi anda boleh melangkau ke bahagian GLSL. Khususnya, teduhan sel dan teduhan Phong mungkin menarik minat anda.
- Dapatkan inspirasi dari demo di GLSL Sandbox dan ShaderToy
Rujukan
Tekstur batu dan peta biasa yang digunakan dalam tutorial ini diambil dari OpenGameArt:
http://opengameart.org/content/50-free-textures-4-normalmaps
Terdapat banyak program yang boleh membantu anda membuat peta normal. Sekiranya anda berminat untuk mempelajari lebih lanjut mengenai cara membuat peta normal anda sendiri, artikel ini boleh membantu.
Envato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post