Unity Interactive Map Shaders

gambar

Tutorial ini adalah tentang peta interaktif dan membuatnya di Unity menggunakan shader.

Efek ini dapat berfungsi sebagai dasar untuk teknik yang lebih kompleks, seperti proyeksi holografik atau bahkan meja pasir dari film "Black Panther".

Sebuah inspirasi untuk tutorial ini adalah tweet yang diterbitkan oleh Baran Kahyaoglu , menunjukkan contoh apa yang ia buat untuk Mapbox .



Adegan (tidak termasuk peta) diambil dari demo Spaceship Grafik Efek Visual Unity (lihat di bawah), yang dapat diunduh di sini .


Bagian 1. Vertex Offset


Efek anatomi


Hal pertama yang Anda dapat segera perhatikan adalah bahwa peta geografis datar : jika mereka digunakan sebagai tekstur, maka mereka tidak memiliki tiga dimensi yang dimiliki oleh model 3D nyata dari area peta yang sesuai.

Anda dapat menerapkan solusi ini: membuat model 3D dari area yang diperlukan dalam game, dan kemudian menerapkan tekstur dari peta ke sana. Ini akan membantu menyelesaikan masalah, tetapi butuh banyak waktu dan tidak akan memungkinkan untuk mewujudkan efek "bergulir" dari video Baran Kahyaoglu.

Jelas, pendekatan yang lebih teknis adalah yang terbaik. Untungnya, shader dapat digunakan untuk mengubah geometri model 3D. Dengan bantuan mereka, Anda dapat mengubah pesawat apa pun menjadi lembah dan pegunungan di wilayah yang kami butuhkan.

Dalam tutorial ini kita menggunakan peta Chillot , Chilli, terkenal dengan bukit-bukit khasnya. Gambar di bawah ini menunjukkan tekstur wilayah diplot pada jala bundar.


Meskipun kita melihat bukit dan gunung, mereka masih benar-benar datar. Ini menghancurkan ilusi realisme.

Normal ekstrusi


Langkah pertama untuk menggunakan shader untuk mengubah geometri adalah teknik yang disebut ekstrusi normal . Dia membutuhkan pengubah simpul : fungsi yang dapat memanipulasi simpul individu dari model 3D.

Cara modifier vertex digunakan tergantung pada jenis shader yang digunakan. Dalam tutorial ini, kita akan mengubah Surface Standard Shader - salah satu jenis shader yang dapat Anda buat di Unity.

Ada banyak cara untuk memanipulasi simpul dari model 3D. Salah satu metode pertama yang dijelaskan dalam sebagian besar tutorial vertex shader adalah mengekstrusi normals . Ini terdiri dari mendorong setiap titik "keluar" ( mengekstrusi ), yang memberikan model 3D tampilan yang lebih besar. "Di luar" berarti bahwa setiap titik bergerak sepanjang arah normal.


Untuk permukaan yang halus, ini bekerja dengan sangat baik, tetapi pada model dengan koneksi titik yang buruk, metode ini dapat membuat artefak aneh. Efek ini dijelaskan dengan baik dalam salah satu tutorial pertama saya: A Gentle Introduction to Shaders , di mana saya menunjukkan cara mengusir dan mengganggu model 3D.


Menambahkan normal diekstrusi ke permukaan shader sangat mudah. Setiap shader permukaan memiliki #pragma , yang digunakan untuk mengirimkan informasi dan perintah tambahan. Salah satu perintah tersebut adalah vert , yang berarti bahwa fungsi vert akan digunakan untuk memproses setiap simpul dari model 3D.

Shader yang diedit adalah sebagai berikut:

 #pragma surface surf Standard fullforwardshadows addshadow vertex:vert ... float _Amount; ... void vert(inout appdata_base v) { v.vertex.xyz += v.normal * _Amount; } 

Karena kita mengubah posisi simpul, kita juga perlu menggunakan addshadow jika kita ingin model untuk membuat bayangan dengan benar.

Apa itu appdata_base?
Seperti yang Anda lihat, kami telah menambahkan fungsi pengubah simpul ( vert ), yang mengambil sebagai parameter struktur yang disebut appdata_base . Struktur ini menyimpan informasi tentang masing-masing simpul individu model 3D. Ini tidak hanya berisi posisi titik ( v.vertex ), tetapi juga bidang lain, misalnya , arah normal ( v.normal ) dan informasi tekstur yang terkait dengan titik ( v.texcoord ).

Dalam beberapa kasus, ini tidak cukup dan kita mungkin memerlukan properti lain, seperti warna titik ( v.color ) dan arah garis singgung ( v.tangent ). Pengubah Vertex dapat ditentukan menggunakan berbagai struktur appdata_tan lainnya, termasuk appdata_tan dan appdata_full , yang menyediakan informasi lebih lanjut dengan biaya appdata_full kinerja rendah. Anda dapat membaca lebih lanjut tentang appdata (dan variasinya) di wiki Unity3D .

Bagaimana nilai dikembalikan dari vert?
Fungsi teratas tidak memiliki nilai balik. Jika Anda terbiasa dengan bahasa C #, Anda harus tahu bahwa struktur dilewatkan oleh nilai, yaitu, ketika v.vertex berubah v.vertex ini hanya memengaruhi salinan v , ruang lingkup yang dibatasi oleh badan fungsi.

Namun, v juga dideklarasikan sebagai inout , yang artinya digunakan untuk input dan output. Setiap perubahan yang Anda lakukan, ubah variabel itu sendiri, yang kami sampaikan ke vert . Kata kunci out dan out sangat sering digunakan dalam grafik komputer, dan secara kasar dapat dikorelasikan dengan ref dan out dalam C #.

Mengekstrusi normals dengan tekstur


Kode yang kami gunakan di atas berfungsi dengan benar, tetapi jauh dari efek yang ingin kami capai. Alasannya adalah bahwa kami tidak ingin mengekstrusi semua simpul dengan jumlah yang sama. Kami ingin permukaan model 3D agar sesuai dengan lembah dan pegunungan di wilayah geografis yang sesuai. Pertama, kita perlu menyimpan dan mengambil informasi tentang seberapa banyak setiap titik pada peta dinaikkan. Kami ingin ekstrusi dipengaruhi oleh tekstur tempat ketinggian lanskap dikodekan. Tekstur seperti itu sering disebut pemetaan ketinggian , tetapi sering juga disebut pemetaan kedalaman , tergantung pada konteksnya. Setelah menerima informasi tentang ketinggian, kami akan dapat memodifikasi ekstrusi pesawat berdasarkan peta ketinggian Seperti yang ditunjukkan dalam diagram, ini akan memungkinkan kita untuk mengontrol kenaikan dan penurunan area.


Sangat sederhana untuk menemukan gambar satelit dari area geografis yang Anda minati dan peta ketinggian terkait. Di bawah ini adalah peta satelit Mars (di atas) dan peta ketinggian (di bawah) yang digunakan dalam tutorial ini:



Saya berbicara secara terperinci tentang konsep peta mendalam dalam serangkaian tutorial lain yang disebut "Foto 3D Facebook dari dalam: parallax shaders" [ terjemahan ke Habré].

Dalam tutorial ini, kita akan mengasumsikan bahwa peta ketinggian disimpan sebagai gambar dalam skala abu-abu, di mana hitam dan putih sesuai dengan ketinggian yang lebih rendah dan lebih tinggi. Kita juga perlu nilai-nilai ini untuk skala secara linear , yaitu, perbedaan warna, misalnya, pada 0.1 sesuai dengan perbedaan tinggi antara 0 dan 0.1 atau antara 0.9 dan 1.0 . Untuk peta kedalaman, ini tidak selalu benar, karena banyak dari mereka menyimpan informasi kedalaman pada skala logaritmik .

Untuk mengambil sampel tekstur, diperlukan dua elemen informasi: tekstur itu sendiri dan koordinat UV dari titik yang ingin kita sampel. Yang terakhir dapat diakses melalui bidang texcoord , disimpan dalam struktur appdata_base . Ini adalah koordinat UV yang terkait dengan simpul saat ini sedang diproses. Pengambilan sampel tekstur pada fungsi permukaan dilakukan menggunakan tex2D , namun ketika kita berada dalam , tex2Dlod diperlukan.

Dalam cuplikan kode di bawah ini, tekstur yang disebut _HeightMap digunakan untuk mengubah nilai ekstrusi yang dilakukan untuk setiap simpul:

 sampler2D _HeightMap; ... void vert(inout appdata_base v) { fixed height = tex2Dlod(_HeightMap, float4(v.texcoord.xy, 0, 0)).r; vertex.xyz += v.normal * height * _Amount; } 

Mengapa tex2D tidak dapat digunakan sebagai fungsi vertex?
Jika Anda melihat kode yang dihasilkan Unity untuk Standard Surface Shader, Anda akan melihat bahwa itu sudah berisi contoh cara sampel tekstur. Secara khusus, sampel tekstur utama (disebut _MainTex ) dalam fungsi permukaan (disebut surf ) menggunakan fungsi tex2D .

Dan pada kenyataannya, tex2D digunakan untuk sampel piksel dari suatu tekstur, terlepas dari apa yang disimpan di dalamnya, warna atau ketinggian. Namun, Anda mungkin memperhatikan bahwa tex2D tidak dapat digunakan dalam fungsi vertex.

Alasannya adalah bahwa tex2D tidak hanya membaca piksel dari tekstur. Dia juga memutuskan versi tekstur yang akan digunakan, tergantung pada jarak ke kamera. Teknik ini disebut mipmapping : memungkinkan Anda untuk memiliki versi yang lebih kecil dari satu tekstur yang dapat secara otomatis digunakan pada jarak yang berbeda.

Dalam fungsi permukaan, shader sudah tahu tekstur MIP mana yang digunakan. Informasi ini mungkin belum tersedia dalam fungsi vertex, dan karena itu tex2D tidak dapat digunakan dengan penuh keyakinan. Berbeda dengan itu, fungsi tex2Dlod dapat melewati dua parameter tambahan, yang dalam tutorial ini dapat memiliki nilai nol.

Hasilnya terlihat jelas pada gambar di bawah ini.



Dalam hal ini, satu penyederhanaan dapat dibuat. Kode yang kami ulas sebelumnya dapat bekerja dengan geometri apa pun. Namun, kita dapat mengasumsikan bahwa permukaannya benar-benar rata. Faktanya, kami benar-benar ingin menerapkan efek ini pada pesawat.

Oleh karena itu, Anda dapat menghapus v.normal dan menggantinya dengan float3(0, 1, 0) :

 void vert(inout appdata_base v) { float3 normal = float3(0, 1, 0); fixed height = tex2Dlod(_HeightMap, float4(v.texcoord.xy, 0, 0)).r; vertex.xyz += normal * height * _Amount; } 

Kita bisa melakukan ini karena semua koordinat di appdata_base disimpan dalam ruang model , yaitu, mereka diatur relatif ke pusat dan orientasi model 3D. Transisi, rotasi, dan penskalaan dengan transformasi dalam Unity mengubah posisi, rotasi, dan skala objek, tetapi tidak memengaruhi model 3D asli.

Bagian 2. Efek Menggulir


Semua yang kami lakukan di atas bekerja dengan sangat baik. Sebelum melanjutkan, kami akan mengekstrak kode yang diperlukan untuk menghitung ketinggian simpul baru ke dalam fungsi getVertex terpisah:

 float4 getVertex(float4 vertex, float2 texcoord) { float3 normal = float3(0, 1, 0); fixed height = tex2Dlod(_HeightMap, float4(texcoord, 0, 0)).r; vertex.xyz += normal * height * _Amount; return vertex; } 

Maka seluruh fungsi vert akan memiliki bentuk:

 void vert(inout appdata_base v) { vertex = getVertex(v.vertex, v.texcoord.xy); } 

Kami melakukan ini karena di bawah ini kami perlu menghitung ketinggian beberapa titik. Karena kenyataan bahwa fungsi ini akan berada dalam fungsinya sendiri yang terpisah, kode akan menjadi lebih sederhana.

Perhitungan koordinat UV


Namun, ini membawa kita ke masalah lain. Fungsi getVertex tidak hanya bergantung pada posisi vertex saat ini (v.vertex), tetapi juga pada koordinat UV-nya ( v.texcoord ).

Ketika kami ingin menghitung ketinggian vertex yang sedang diproses fungsi vert , kedua elemen data tersedia di struktur appdata_base . Namun, apa yang terjadi jika kita perlu mengambil sampel posisi titik tetangga? Dalam hal ini, kita dapat mengetahui posisi xyz di ruang model , tetapi kita tidak memiliki akses ke koordinat UV-nya.

Ini berarti bahwa sistem yang ada hanya dapat menghitung tinggi offset hanya untuk verteks saat ini. Pembatasan seperti itu tidak akan memungkinkan kita untuk melanjutkan, jadi kita perlu menemukan solusi.

Cara termudah adalah menemukan cara untuk menghitung koordinat UV dari objek 3D, mengetahui posisi verteksnya. Ini adalah tugas yang sangat sulit, dan ada beberapa teknik untuk menyelesaikannya (salah satu yang paling populer adalah proyeksi triplanar ). Tetapi dalam kasus khusus ini, kita tidak perlu mencocokkan UV dengan geometri. Jika kita mengasumsikan bahwa shader akan selalu diterapkan pada flat mesh, maka tugasnya menjadi sepele.

Kita dapat menghitung koordinat UV (gambar bawah) dari posisi simpul (gambar atas) karena fakta bahwa keduanya ditumpangkan secara linear pada mesh datar.



Ini berarti bahwa untuk menyelesaikan masalah kita, kita perlu mengubah komponen XZ dari posisi vertex menjadi koordinat UV yang sesuai.


Prosedur ini disebut interpolasi linier . Itu dibahas secara rinci di situs web saya (misalnya: Rahasia Interpolasi Warna ).

Dalam kebanyakan kasus, nilai-nilai UV berada dalam kisaran dari 0sebelumnya 1; Sebaliknya, koordinat masing-masing simpul berpotensi tidak terbatas. Dari sudut pandang matematika, untuk konversi dari XZ ke UV, kita hanya perlu nilai batasnya:

  • Xmin, Xmaks
  • Zmin, Zmaks
  • Umin, Umaks
  • Vmin, Vmaks

yang ditunjukkan di bawah ini:


Nilai-nilai ini bervariasi tergantung pada mesh yang digunakan. Pada bidang Unity, koordinat UV berada dalam kisaran dari 0sebelumnya 1, dan koordinat simpul berada dalam kisaran dari 5sebelumnya +5.

Persamaan untuk mengubah XZ ke UV adalah:

(1)
gambar


Bagaimana mereka ditampilkan?
Jika Anda tidak terbiasa dengan konsep interpolasi linier, maka persamaan ini mungkin tampak cukup menakutkan.

Namun, mereka ditampilkan dengan cukup sederhana. Mari kita lihat contoh saja. U. Kami memiliki dua interval: satu memiliki nilai dari Xminsebelumnya Xmakslain dari Uminsebelumnya Umaks. Data yang masuk untuk koordinat Xadalah koordinat dari simpul saat ini sedang diproses, dan output akan menjadi koordinat Udigunakan untuk sampel tekstur.

Kita perlu mempertahankan sifat proporsionalitas antara Xdan intervalnya, dan Udan intervalnya. Misalnya, jika Xpenting 25% dari intervalnya Ujuga akan berpengaruh 25% dari intervalnya.

Semua ini ditunjukkan pada diagram berikut:


Dari sini kita dapat menyimpulkan bahwa proporsi yang terdiri dari segmen merah sehubungan dengan merah muda harus sama dengan proporsi antara segmen biru dan biru:

(2)

Sekarang kita bisa mengubah persamaan yang ditunjukkan di atas untuk mendapatkan U:


dan persamaan ini memiliki bentuk yang persis sama seperti yang ditunjukkan di atas (1).

Persamaan ini dapat diimplementasikan dalam kode sebagai berikut:

 float2 _VertexMin; float2 _VertexMax; float2 _UVMin; float2 _UVMax; float2 vertexToUV(float4 vertex) { return (vertex.xz - _VertexMin) / (_VertexMax - _VertexMin) * (_UVMax - _UVMin) + _UVMin; } 

Sekarang kita dapat memanggil fungsi getVertex tanpa harus meneruskan v.texcoord :

 float4 getVertex(float4 vertex) { float3 normal = float3(0, 1, 0); float2 texcoord = vertexToUV(vertex); fixed height = tex2Dlod(_HeightMap, float4(texcoord, 0, 0)).r; vertex.xyz += normal * height * _Amount; return vertex; } 

Kemudian seluruh fungsi vert mengambil bentuk:

 void vert(inout appdata_base v) { v.vertex = getVertex(v.vertex); } 

Efek gulir


Berkat kode yang kami tulis, seluruh peta ditampilkan di mesh. Jika kita ingin meningkatkan tampilan, maka kita perlu melakukan perubahan.

Mari kita memformalkan kode sedikit lagi. Pertama, kita mungkin perlu memperbesar bagian peta yang terpisah, daripada melihatnya secara keseluruhan.


Area ini dapat didefinisikan oleh dua nilai: ukurannya ( _CropSize ) dan lokasi pada peta ( _CropOffset ), diukur dalam ruang simpul (dari _VertexMin ke _VertexMax ).

 // Cropping float2 _CropSize; float2 _CropOffset; 

Setelah menerima kedua nilai ini, kami dapat sekali lagi menggunakan interpolasi linier sehingga getVertex dipanggil bukan untuk posisi saat ini di bagian atas model 3D, tetapi untuk titik yang diskalakan dan ditransfer.


Kode yang relevan:

 void vert(inout appdata_base v) { float2 croppedMin = _CropOffset; float2 croppedMax = croppedMin + _CropSize; // v.vertex.xz: [_VertexMin, _VertexMax] // cropped.xz : [croppedMin, croppedMax] float4 cropped = v.vertex; cropped.xz = (v.vertex.xz - _VertexMin) / (_VertexMax - _VertexMin) * (croppedMax - croppedMin) + croppedMin; v.vertex.y = getVertex(cropped); } 

Jika kita ingin menggulir, maka cukup untuk memperbarui _CropOffset melalui skrip. Karena ini, area pemotongan akan bergerak, sebenarnya menggulir lanskap.

 public class MoveMap : MonoBehaviour { public Material Material; public Vector2 Speed; public Vector2 Offset; private int CropOffsetID; void Start () { CropOffsetID = Shader.PropertyToID("_CropOffset"); } void Update () { Material.SetVector(CropOffsetID, Speed * Time.time + Offset); } } 

Agar ini berfungsi, sangat penting untuk mengatur Mode Bungkus semua tekstur menjadi Ulangi . Jika ini tidak dilakukan, maka kita tidak akan dapat mengulangi tekstur.

Untuk efek zoom / zoom, cukup mengubah _CropSize .

Bagian 3. Naungan medan


Naungan datar


Semua kode yang kami tulis berfungsi, tetapi memiliki masalah serius. Model bayangan entah bagaimana aneh. Permukaannya melengkung dengan benar, tetapi bereaksi terhadap cahaya seolah-olah datar.

Ini sangat jelas terlihat pada gambar di bawah ini. Gambar atas menunjukkan shader yang ada; bagian bawah menunjukkan cara kerjanya.



Memecahkan masalah ini bisa menjadi tantangan besar. Tapi pertama-tama, kita perlu mencari tahu apa kesalahannya.

Operasi ekstrusi normal mengubah geometri umum dari pesawat yang kami gunakan awalnya. Namun, Unity hanya mengubah posisi simpul, tetapi tidak arah normal mereka. Arah vertex normal , seperti namanya, adalah vektor satuan panjang ( arah ) yang mengindikasikan tegak lurus terhadap permukaan. Normal diperlukan karena mereka memainkan peran penting dalam menaungi model 3D. Mereka digunakan oleh semua permukaan shader untuk menghitung bagaimana cahaya harus dipantulkan dari setiap segitiga model 3D. Biasanya ini diperlukan untuk meningkatkan tiga dimensi dari model, misalnya, itu menyebabkan cahaya memantul dari permukaan datar sama seperti itu akan memantul dari permukaan melengkung. Trik ini sering digunakan untuk membuat permukaan poli-rendah terlihat lebih halus daripada yang sebenarnya (lihat di bawah).


Namun, dalam kasus kami yang sebaliknya terjadi. Geometri melengkung dan halus, tetapi karena semua normals diarahkan ke atas, cahaya dipantulkan dari model seolah-olah datar (lihat di bawah):


Anda dapat membaca lebih lanjut tentang peran normals dalam bayangan objek dalam artikel tentang Pemetaan Normal (Bump Mapping) , di mana silinder identik terlihat sangat berbeda, meskipun model 3D yang sama, karena berbagai metode penghitungan normals vertex (lihat di bawah).



Sayangnya, baik Unity maupun bahasa untuk membuat shader tidak memiliki solusi bawaan untuk menghitung ulang normals secara otomatis. Ini berarti Anda harus mengubahnya secara manual tergantung pada geometri lokal model 3D.

Perhitungan normal


Satu-satunya cara untuk memperbaiki masalah bayangan adalah dengan menghitung secara manual normals berdasarkan geometri permukaan. Tugas serupa dibahas dalam sebuah posting oleh Vertex Displacement - Melting Shader Bagian 1 , di mana ia digunakan untuk mensimulasikan peleburan model 3D dalam Cone Wars .

Meskipun kode yang sudah selesai harus bekerja dalam koordinat 3D, mari kita batasi tugas hanya dua dimensi untuk saat ini. Bayangkan Anda perlu menghitung arah normal yang sesuai dengan titik pada kurva 2D (panah biru besar pada diagram di bawah).


Dari sudut pandang geometris, arah normal (panah biru besar) adalah vektor tegak lurus terhadap garis singgung yang melewati titik yang menarik bagi kita (garis biru tipis). Garis singgung dapat direpresentasikan sebagai garis yang terletak pada kelengkungan model. Vektor singgung adalah vektor satuan yang terletak pada singgung.

Ini berarti bahwa untuk menghitung normal, Anda perlu mengambil dua langkah: pertama, temukan garis singgung garis ke titik yang diinginkan; kemudian hitung vektor tegak lurus terhadapnya (yang akan menjadi arah yang diperlukan dari normal ).

Perhitungan singgung


Untuk mendapatkan yang normal, pertama-tama kita harus menghitung garis singgung . Ini dapat diperkirakan dengan mengambil sampel titik terdekat dan menggunakannya untuk membangun garis di dekat titik. Semakin kecil garis, semakin akurat nilainya.

Dibutuhkan tiga langkah:

  • Tahap 1. Pindahkan sedikit pada permukaan yang rata
  • Langkah 2. Hitung ketinggian titik baru.
  • Langkah 3. Gunakan ketinggian titik saat ini untuk menghitung garis singgung

Semua ini bisa dilihat pada gambar di bawah ini:


Agar ini berfungsi, kita perlu menghitung ketinggian dua poin, bukan satu. Untungnya, kita sudah tahu bagaimana melakukan ini. Pada bagian sebelumnya dari tutorial, kami membuat fungsi yang mengambil sampel ketinggian lanskap berdasarkan titik jala. Kami menyebutnya getVertex .

Kita dapat mengambil nilai titik baru pada titik saat ini, dan kemudian pada dua titik lainnya. Satu akan untuk garis singgung, yang lain untuk garis singgung di dua titik. Dengan bantuan mereka, kita mendapatkan yang normal. Jika mesh asli yang digunakan untuk membuat efeknya rata (dan dalam kasus kami adalah), maka kita tidak perlu akses ke v.normal dan kita bisa menggunakan float3(0, 0, 1) untuk tangen dan tangen ke dua titik, masing-masing float3(0, 0, 1) dan float3(1, 0, 0) . Jika kita ingin melakukan hal yang sama, tetapi, misalnya, untuk bola, maka akan jauh lebih sulit untuk menemukan dua titik yang cocok untuk menghitung garis singgung dan garis singgung menjadi dua titik.

Karya seni vektor


Setelah memperoleh vektor garis singgung dan garis singgung yang cocok untuk dua titik, kita dapat menghitung normal menggunakan operasi yang disebut produk vektor . Ada banyak definisi dan penjelasan tentang karya vektor dan apa fungsinya.

Produk vektor menerima dua vektor dan mengembalikan satu vektor baru. Jika dua vektor awal adalah satuan (panjangnya sama dengan satu), dan mereka berada pada sudut 90, maka vektor yang dihasilkan akan berada pada 90 derajat relatif terhadap keduanya.

Pada awalnya, ini dapat membingungkan, tetapi secara grafis dapat direpresentasikan sebagai berikut: produk vektor dari dua sumbu menciptakan yang ketiga. Yaitu X kaliY=Ztapi juga X kaliZ=Y, dan seterusnya.

Jika kita mengambil langkah yang cukup kecil (dalam kode, ini offset ), maka vektor garis singgung dan garis singgung ke dua titik akan berada pada sudut 90 derajat.Bersama-sama dengan vektor normal, mereka membentuk tiga sumbu tegak lurus yang berorientasi di sepanjang permukaan model.

Mengetahui hal ini, kita dapat menulis semua kode yang diperlukan untuk menghitung dan memperbarui vektor normal.

 void vert(inout appdata_base v) { float3 bitangent = float3(1, 0, 0); float3 tangent = float3(0, 0, 1); float offset = 0.01; float4 vertexBitangent = getVertex(v.vertex + float4(bitangent * offset, 0) ); float4 vertex = getVertex(v.vertex); float4 vertexTangent = getVertex(v.vertex + float4(tangent * offset, 0) ); float3 newBitangent = (vertexBitangent - vertex).xyz; float3 newTangent = (vertexTangent - vertex).xyz; v.normal = cross(newTangent, newBitangent); v.vertex.y = vertex.y; } 

Menyatukan semuanya


Sekarang semuanya berfungsi, kita dapat mengembalikan efek gulir.

 void vert(inout appdata_base v) { // v.vertex.xz: [_VertexMin, _VertexMax] // cropped.xz : [croppedMin, croppedMax] float2 croppedMin = _CropOffset; float2 croppedMax = croppedMin + _CropSize; float4 cropped = v.vertex; cropped.xz = (v.vertex.xz - _VertexMin) / (_VertexMax - _VertexMin) * (croppedMax - croppedMin) + croppedMin; float3 bitangent = float3(1, 0, 0); float3 normal = float3(0, 1, 0); float3 tangent = float3(0, 0, 1); float offset = 0.01; float4 vertexBitangent = getVertex(cropped + float4(bitangent * offset, 0) ); float4 vertex = getVertex(cropped); float4 vertexTangent = getVertex(cropped + float4(tangent * offset, 0) ); float3 newBitangent = (vertexBitangent - vertex).xyz; float3 newTangent = (vertexTangent - vertex).xyz; v.normal = cross(newTangent, newBitangent); v.vertex.y = vertex.y; v.texcoord = float4(vertexToUV(cropped), 0,0); } 

Dan tentang ini efek kita akhirnya selesai.


Ke mana harus pergi selanjutnya


Tutorial ini dapat menjadi dasar dari efek yang lebih kompleks, misalnya, proyeksi holografik atau bahkan salinan tabel pasir dari film "Black Panther".


Paket Persatuan


Paket lengkap untuk tutorial ini dapat diunduh di Patreon , berisi semua aset yang diperlukan untuk memainkan efek yang dijelaskan.

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


All Articles