Saat bekerja dengan aset poligon, Anda hanya dapat menggambar satu objek pada satu waktu (jika Anda tidak memperhitungkan teknik seperti batching dan instancing), tetapi jika Anda menggunakan bidang jarak dengan tanda (bidang jarak yang ditandatangani, SDF), maka kami tidak terbatas pada hal ini. Jika dua posisi memiliki koordinat yang sama, maka fungsi jarak yang ditandatangani akan mengembalikan nilai yang sama, dan dalam satu perhitungan kita bisa mendapatkan beberapa angka. Untuk memahami cara mengubah ruang yang digunakan untuk menghasilkan bidang jarak yang ditandatangani, saya sarankan
Anda mencari cara
membuat bentuk menggunakan fungsi jarak yang ditandatangani dan
menggabungkan bentuk sdf .
Konfigurasi
Untuk tutorial ini, saya memodifikasi pasangan antara kuadrat dan lingkaran, tetapi Anda dapat menggunakannya untuk bentuk lainnya. Ini mirip dengan konfigurasi untuk
tutorial sebelumnya .
Penting di sini bahwa bagian yang dapat dimodifikasi adalah sebelum menggunakan posisi untuk menghasilkan angka.
Shader "Tutorial/036_SDF_Space_Manpulation/Type"{ Properties{ _InsideColor("Inside Color", Color) = (.5, 0, 0, 1) _OutsideColor("Outside Color", Color) = (0, .5, 0, 1) _LineDistance("Mayor Line Distance", Range(0, 2)) = 1 _LineThickness("Mayor Line Thickness", Range(0, 0.1)) = 0.05 [IntRange]_SubLines("Lines between major lines", Range(1, 10)) = 4 _SubLineThickness("Thickness of inbetween lines", Range(0, 0.05)) = 0.01 } SubShader{
Dan fungsi 2D_SDF.cginc terletak di folder yang sama dengan shader, yang akan kita kembangkan, pada awalnya terlihat seperti ini:
#ifndef SDF_2D #define SDF_2D
Pengulangan ruang
Refleksi cermin
Salah satu operasi paling sederhana adalah mencerminkan dunia tentang sumbu. Untuk memantulkannya di sekitar sumbu y, kita mengambil nilai absolut dari komponen x posisi kita. Dengan demikian, koordinat ke kanan dan kiri poros akan sama.
(-1, 1)
berubah menjadi
(1, 1)
, dan ternyata berada di dalam lingkaran menggunakan
(1, 1)
sebagai asal koordinat dan dengan jari-jari lebih besar dari 0.
Paling sering, kode yang menggunakan fungsi ini akan terlihat seperti
position = mirror(position);
jadi kita bisa menyederhanakannya sedikit. Kami hanya akan mendeklarasikan argumen posisi sebagai inout. Jadi, saat menulis ke argumen, itu juga akan mengubah variabel yang kita lewatkan ke fungsi. Nilai kembalian dapat berupa tipe void, karena kita masih belum menggunakan nilai kembalian.
Ternyata sudah cukup bagus, tapi dengan cara ini kita hanya mendapatkan satu sumbu untuk mirroring. Kita dapat memperluas fungsi dengan memutar ruang seperti yang kita lakukan saat memutar angka. Pertama, Anda perlu memutar ruang, lalu cermin, dan lalu balikkan. Dengan cara ini kita dapat melakukan mirroring dengan memperhatikan sudut mana pun. Hal yang sama dimungkinkan ketika mentransfer ruang dan melakukan transfer balik setelah mirroring. (Jika Anda melakukan kedua operasi, maka sebelum mirroring, jangan lupa untuk melakukan transfer terlebih dahulu, dan kemudian berbelok, setelah itu belok pertama.)
Sel
Jika Anda tahu cara kerja
noise , maka Anda memahami bahwa untuk generasi prosedural kita sering mengulangi posisi dan mendapatkan sel-sel kecil yang pada dasarnya sama, hanya berbeda dalam parameter yang tidak signifikan. Kita dapat melakukan hal yang sama untuk bidang jarak.
Karena fungsi
fmod
(serta menggunakan% untuk membagi dengan sisanya) memberi kita sisanya, bukan definisi sisanya, kita harus menggunakan trik. Pertama, kita mengambil sisa pembagian integer dengan fungsi fmod. Untuk bilangan positif, ini adalah persis apa yang kita butuhkan, dan untuk bilangan negatif ini adalah hasil yang kita butuhkan dikurangi periode. Anda dapat memperbaikinya dengan menambahkan periode dan sekali lagi mengambil sisa divisi. Menambahkan periode akan memberikan hasil yang diinginkan untuk nilai input negatif, dan untuk nilai input positif, nilainya satu periode lebih tinggi. Sisa kedua divisi ini tidak akan melakukan apa pun dengan nilai-nilai untuk nilai input negatif, karena mereka sudah dalam kisaran dari 0 hingga periode, dan untuk nilai input positif kami pada dasarnya mengurangi satu periode.

Masalah dengan sel adalah bahwa kita kehilangan kontinuitas yang kita suka bidang jarak. Ini tidak buruk jika bentuknya hanya di tengah sel, tetapi dalam contoh yang ditunjukkan di atas ini dapat menyebabkan artefak yang signifikan yang harus dihindari ketika bidang jarak digunakan untuk banyak tugas di mana bidang jarak biasanya dapat diterapkan.
Ada satu solusi yang tidak bekerja di setiap kasus, tetapi ketika berhasil, itu luar biasa: untuk mencerminkan setiap sel lainnya. Untuk melakukan ini, kita memerlukan indeks sel piksel, tetapi kita masih belum memiliki nilai balik dalam fungsinya, jadi kita bisa menggunakannya untuk mengembalikan indeks sel.
Untuk menghitung indeks sel, kami membagi posisi berdasarkan periode. Jadi, 0-1 adalah sel pertama, 1-2 adalah yang kedua, dan seterusnya ... dan kita dapat dengan mudah mendiskresikannya. Untuk mendapatkan indeks sel, kita cukup membulatkan nilainya ke bawah dan mengembalikan hasilnya. Yang penting adalah kita menghitung indeks sel sebelum membaginya dengan sisanya untuk mengulangi sel; jika tidak, kita akan mendapatkan indeks 0 di mana-mana, karena posisinya tidak dapat melebihi periode.
Dengan informasi ini, kita dapat membalik sel. Untuk memahami apakah akan membalik atau tidak, kita membagi modulo indeks sel 2. Hasil operasi ini adalah bergantian 0 dan 1 atau -1 setiap sel kedua. Untuk menjadikan perubahan lebih permanen, kami mengambil nilai absolut dan mendapatkan nilai yang beralih antara 0 dan 1.
Untuk menggunakan nilai ini untuk beralih antara posisi normal dan terbalik, kita memerlukan fungsi yang tidak melakukan apa pun untuk nilai 0, dan kurangi posisi dari periode di mana flipping adalah 1. Artinya, kami melakukan interpolasi linier dari normal ke posisi membalik menggunakan variabel flip. . Karena variabel flip adalah vektor 2d, komponen-komponennya dibalik satu per satu.
Sel radial
Fitur hebat lainnya adalah pengulangan ruang dalam pola radial.
Untuk mendapatkan efek ini, pertama-tama kita menghitung posisi radial. Untuk melakukan ini, kami mengkodekan sudut relatif ke pusat sumbu x dan jarak dari pusat sepanjang sumbu y.
float2 radialPosition = float2(atan2(position.x, position.y), length(position));
Lalu kami ulangi sudutnya. Karena mentransmisikan jumlah pengulangan jauh lebih mudah daripada sudut setiap potongan, pertama-tama kita menghitung ukuran setiap potongan. Seluruh lingkaran adalah 2 * pi, jadi untuk mendapatkan bagian yang tepat, kami membagi 2 * pi dengan ukuran sel.
const float PI = 3.14159; float cellSize = PI * 2 / cells;
Dengan informasi ini, kita dapat mengulangi komponen x posisi radial setiap unit cellSize. Kami melakukan pengulangan dengan pembagian dengan sisanya, oleh karena itu, seperti sebelumnya, kami mendapatkan masalah dengan angka negatif, yang dapat dihilangkan dengan bantuan dua fungsi pembagian dengan sisanya.
radialPosition.x = fmod(fmod(radialPosition.x, cellSize) + cellSize, cellSize);
Maka Anda perlu memindahkan posisi baru kembali ke koordinat xy yang biasa. Di sini kita menggunakan fungsi sincos dengan komponen x posisi radial sebagai sudut untuk menulis sinus ke koordinat x posisi dan cosinus ke koordinat y. Dengan langkah ini kita mendapatkan posisi yang dinormalisasi. Untuk mendapatkan arah yang benar dari pusat, Anda perlu mengalikannya dengan komponen y dari posisi radial, yang berarti panjangnya.
Kemudian kita juga bisa menambahkan indeks sel dan mirroring, seperti yang kita lakukan dengan sel biasa.
Penting untuk menghitung indeks sel setelah menghitung posisi radial, tetapi sebelum menerima sisanya dari divisi. Kami mendapatkannya dengan membagi komponen x dari posisi radial dan membulatkan hasilnya ke bawah. Dalam hal ini, indeks juga bisa negatif, dan ini merupakan masalah jika jumlah selnya ganjil. Misalnya, dengan 3 sel, kita mendapatkan 1 sel dengan indeks 0, 1 sel dengan indeks -1 dan 2 sel setengah dengan indeks 1 dan -2. Untuk mengatasi masalah ini, kami menambahkan jumlah sel ke variabel dibulatkan ke bawah variabel, dan kemudian dibagi dengan ukuran sel dengan sisanya.
Untuk mencerminkan ini, kita perlu koordinat yang ditentukan dalam radian, jadi untuk menghindari penghitungan ulang koordinat radial di luar fungsi, kita akan menambahkan opsi untuk itu menggunakan argumen bool. Biasanya dalam shader, percabangan (jika konstruksi) tidak diterima, tetapi dalam hal ini semua piksel pada layar akan mengikuti jalur yang sama, jadi ini normal.
Mirroring harus terjadi setelah koordinat radial dilingkarkan, tetapi sebelum dikonversi kembali ke posisi normal. Kami mencari tahu apakah kami perlu membalik sel saat ini dengan membagi indeks sel dengan 2 dengan sisanya. Biasanya ini akan memberi kita nol dan satu, tetapi dalam kasus saya beberapa pasangan muncul, yang aneh, namun kita dapat menanganinya. Untuk menghilangkan deuces, kita cukup mengurangi 1 dari variabel flip, dan kemudian mengambil nilai absolut. Jadi, nol dan deuces menjadi satu, dan satuan menjadi nol, seperti yang kita butuhkan, hanya dalam urutan terbalik.
Karena nol dan yang berada di urutan yang salah, kami melakukan interpolasi linier dari versi terbalik ke versi terbalik, dan bukan sebaliknya, seperti sebelumnya. Untuk membalikkan koordinat, kita cukup kurangi posisi dari ukuran sel.
Ruang berayun
Tetapi untuk mengubah ruang tidak perlu mengulanginya. Misalnya, dalam tutorial tentang dasar-dasar, kami memutar, memindahkan, dan menskalanya. Anda juga dapat melakukan hal berikut: pindahkan setiap sumbu berdasarkan yang lain dengan gelombang sinus. Ini akan membuat jarak dari fungsi jarak yang ditandatangani kurang akurat, tetapi sampai mereka terlalu banyak berayun, semuanya akan baik-baik saja.
Pertama, kita menghitung besarnya perubahan posisi dengan membalik komponen x dan y, dan kemudian mengalikannya dengan frekuensi goyangan. Lalu kita ambil sinus dari nilai ini dan kalikan dengan jumlah goyangan yang ingin kita tambahkan. Setelah itu, kita cukup menambahkan faktor goyangan ini ke posisi dan lagi menerapkan hasilnya ke posisi.
Kami juga dapat menghidupkan gelombang ini, mengubah posisinya, menerapkan gerakan melambai pada posisi offset dan mengembalikan ruang kembali. Agar angka floating point tidak menjadi terlalu besar, saya melakukan pembagian dengan sisa pi * 2 dengan frekuensi goyangan, ini berkorelasi dengan goyangan (sinusoid mengulangi setiap pi * 2 unit), jadi kami menghindari lompatan dan offset yang terlalu besar.
Kode sumber
Perpustakaan SDF 2D
#ifndef SDF_2D #define SDF_2D
Demo shader dasar
Shader "Tutorial/036_SDF_Space_Manpulation/Mirror"{ Properties{ _InsideColor("Inside Color", Color) = (.5, 0, 0, 1) _OutsideColor("Outside Color", Color) = (0, .5, 0, 1) _LineDistance("Mayor Line Distance", Range(0, 2)) = 1 _LineThickness("Mayor Line Thickness", Range(0, 0.1)) = 0.05 [IntRange]_SubLines("Lines between major lines", Range(1, 10)) = 4 _SubLineThickness("Thickness of inbetween lines", Range(0, 0.05)) = 0.01 } SubShader{
Sekarang Anda tahu semua dasar-dasar fungsi jarak tanda yang bisa saya ingat. Dalam tutorial berikutnya, saya akan mencoba melakukan sesuatu yang menarik dengan mereka.