Tekstur animasi untuk model; Bagaimana cara menulis shader?

9

Sedang menonton Liga Rocket dan melihat ada stiker dan roda beranimasi.

Gambar.

Saya ingin menerapkan sesuatu yang mirip dengan efek pada gambar di atas.

Bagaimana saya bisa menulis Unity Shader untuk melakukan efek roda?

Saya tidak tahu banyak tentang Shader, tetapi bisakah saya mengedit standar Unity Shader untuk melakukan efek animasi?

JacketPotatoeFan
sumber
3
Shader jelas merupakan solusi masuk ke sini, bahkan untuk sesuatu yang sederhana seperti tekstur gulir. Unity memiliki _Timevariabel bawaan yang dapat Anda tambahkan ke koordinat tekstur dalam shader (vertex) untuk mendapatkan efek gulir yang murah. Efek hexagon akan sangat mudah juga. Jika Anda mengedit pertanyaan Anda untuk menyoroti hanya satu efek dan bertanya "bagaimana saya menerapkan ini dalam shader Persatuan," kami mungkin dapat membantu Anda. Pastikan untuk menentukan apakah shader perlu merespons pencahayaan / bayangan, karena hal itu berdampak pada bagaimana hal itu akan ditulis.
DMGregory
Terimakasih atas infonya. Telah mengubah pertanyaan saya, apakah itu sedikit lebih baik? Saya tidak benar-benar tahu tentang pencahayaan dan bayangan, jadi saya meninggalkannya untuk saat ini sampai saya mendapatkan pemahaman yang lebih baik tentang Shader. Saya kira saya ingin mereka, tetapi saya bisa menyalin bagian dari standar shader bukan?
JacketPotatoeFan
3
Saya akan mengembangkannya dalam jawaban sedikit nanti malam (meskipun merasa bebas untuk mengalahkan saya untuk itu, jika ada yang mau menjawab sekarang!) - tetapi Anda biasanya akan menulis Unity shader yang menggunakan pencahayaan sebagai "Surface Shader" sementara yang tidak membutuhkan pencahayaan seringkali lebih mudah untuk ditulis sebagai "Unlit Shader". Bagiku, roda-roda itu tidak menyala (bayangan sedikit di tepinya terlihat seperti SSAO), jadi aku akan meninggalkan pencahayaan untuk gambar sebagai permulaan. Bisakah Anda mengklarifikasi: apakah Anda menginginkan pola geometris yang ditampilkan di sini, atau kemampuan untuk menggulirkan tekstur sewenang-wenang ke luar & mewarnai seperti itu?
DMGregory
Terima kasih banyak, info apa pun yang dapat Anda berikan untuk memulai saya akan sangat bagus. Saya benar-benar mengagumi orang-orang yang dapat menulis shader, saya telah melihat beberapa artikel untuk Unity Shaders, dan yeah, sangat membingungkan. Saya pikir hanya menggulir melalui tekstur (atau tekstur uvs?) Akan cukup baik, dan dengan kemampuan untuk mewarnai.
JacketPotatoeFan

Jawaban:

13

Saya akan membangun ini dalam beberapa lapisan sehingga Anda dapat melihat bagaimana ia datang bersama-sama.

Mulailah dengan membuat shader baru di Unity dengan memilih Create --> Shader --> Unlitmenu Aset atau menu konteks klik kanan di jendela Proyek.

Di blok atas kami akan menambahkan _ScrollSpeedsparameter untuk mengontrol seberapa cepat tekstur bergerak di setiap arah:

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}

Ini memperlihatkan properti float 4-komponen baru di inspektur material, dengan nama ramah "Kecepatan Gulir" (mirip dengan menambahkan publicatau Serializedvariabel ke MonoBehaviourskrip)

Selanjutnya kita akan menggunakan variabel ini di Vertex shader untuk menggeser koordinat tekstur ( o.uv), dengan menambahkan hanya dua baris ke shader default:

sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our new parameter here so it's visible to the CG shader
float4 _ScrollSpeeds;

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);

    // Shift the uvs over time.
    o.uv += _ScrollSpeeds * _Time.x;

    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}

Menampar itu pada quad (dengan tekstur jerapah gratis yang indah oleh Kenney ) dan Anda mendapatkan:

Quad dengan jerapah berulang yang menggulir di atasnya.

Untuk mendapatkan tekstur menggulir ke luar dalam sebuah cincin, kita bisa menggunakan mesh yang dibagi seperti spiderweb, dengan koordinat uv v meningkat dari tengah ke luar. Tapi itu akan memberikan beberapa artefak berbentuk gergaji sendiri. Sebagai gantinya, saya akan menunjukkan bagaimana kita dapat menghitung UV per fragmen.

Ini sedikit lebih mahal, baik karena operasi trigonometri & panjang (yang lebih mahal daripada matematika dasar) dan karena itu tidak seefisien untuk memprediksi & menyimpan data tekstur dalam perangkat keras ketika menghitung texcoords per fragmen, dibandingkan dengan hanya menginterpolasi mereka. antar simpul. Tetapi untuk efek khusus kecil seperti ini, tidak berlebihan

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);

    // Shift the UVs so (0, 0) is in the middle of the quad.
    o.uv = v.uv - 0.5f;

    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    // Convert our texture coordinates to polar form:
    float2 polar = float2(
           atan2(i.uv.y, i.uv.x)/(2.0f * 3.141592653589f), // angle
           length(i.uv)                                    // radius
        );

    // Apply texture scale
    polar *= _MainTex_ST.xy;

    // Scroll the texture over time.
    polar += _ScrollSpeeds.xy * _Time.x;

    // Sample using the polar coordinates, instead of the original uvs.
    // Here I multiply by MainTex
    fixed4 col = tex2D(_MainTex, polar);

    // apply fog
    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}

Itu memberi kita sesuatu seperti ini (di sini saya meningkatkan parameter ubin dalam materi sehingga lebih jelas apa yang terjadi - hanya dengan mengulangi satu kali ubin di sekitar lingkaran terlihat terdistorsi dan aneh)

Ubin jerapah memancar keluar dari pusat quad

Terakhir, untuk mewarnai tekstur dengan gradien pengguliran, kita bisa menambahkan gradien sebagai tekstur kedua dan mengalikannya menjadi satu.

Pertama kita tambahkan parameter tekstur baru di atas:

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _TintTex("Tint Texture", 2D) = "white" {}
    _ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}

Dan mendeklarasikannya di blok CGPROGRAM kami sehingga CG shader dapat melihatnya:

sampler2D _MainTex;
float4 _MainTex_ST;

// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;

float4 _ScrollSpeeds;

Kemudian perbarui shader fragmen kami untuk menggunakan kedua tekstur:

fixed4 frag(v2f i) : SV_Target
{
    float2 polar = float2(
           atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
           length(i.uv)                                    // radius
        );

    // Copy the polar coordinates before we scale & shift them,
    // so we can scale & shift the tint texture independently.
    float2 tintUVs = polar * _TintTex_ST.xy;
    tintUVs += _ScrollSpeeds.zw * _Time.x;

    polar *= _MainTex_ST.xy;
    polar += _ScrollSpeeds.xy * _Time.x;

    fixed4 col = tex2D(_MainTex, polar);
    // Tint the colour by our second texture.
    col *= tex2D(_TintTex, tintUVs);

    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}

Dan sekarang jerapah kita menjadi sangat trippy:

jauh keluar, bung ....

Dengan pemilihan tekstur dan laju gulir yang sedikit lebih artistik, ini dapat membuat efek yang sangat mirip dengan yang ditunjukkan dalam pertanyaan.


Anda mungkin melihat dua artefak kecil dengan versi yang saya perlihatkan di atas:

  • Wajah-wajah di dekat pusat lingkaran menjadi melar / kurus / runcing, kemudian ketika mereka bergerak ke arah luar, mereka tergencet / melebar.

    Distorsi ini terjadi karena kami memiliki jumlah wajah yang tetap di sekeliling perimeter, tetapi keliling yang mereka rentang semakin lebar seiring dengan meningkatnya jari-jari, sementara tingginya tetap sama.

    Kita dapat memperbaikinya dengan memetakan kembali komponen vertikal dari sampel tekstur untuk mengikuti kurva logaritmik, sehingga pengulangan tekstur semakin terpisah ketika jari-jari meningkat, dan lebih dekat bersama-sama menuju pusat. (Bahkan, ini memberi kita kemunduran jerapah yang lebih kecil dan lebih kecil ...)

  • Ada deretan satu atau dua piksel buram di sepanjang kiri tengah quad.

    Ini terjadi karena GPU melihat dua koordinat sampel tekstur yang berdekatan untuk mencari tahu penyaringan apa yang digunakan. Ketika sampel berdekatan, itu menunjukkan tekstur sedang ditampilkan besar / dekat dan menunjukkan tingkat mip paling rinci. Ketika sampel berjauhan, kita harus menunjukkan tekstur pada zoom kecil atau jauh, dan sampel dari mipmap yang lebih kecil / blur untuk memastikan kita tidak mendapatkan artefak aliasing yang berkilau.

    Masalahnya ada di sini, kita berada di titik pembungkus dalam koordinat kutub, dari -180 hingga 180 derajat. Jadi kita benar-benar mengambil sampel dari titik-titik yang sangat mirip di ruang tekstur berulang kami, bahkan jika koordinat numerik mereka membuatnya terlihat seperti mereka berjauhan. Jadi kami dapat menyediakan vektor gradien sampling kami sendiri untuk memperbaikinya.

Inilah versi dengan koreksi ini:

fixed4 frag(v2f i) : SV_Target
{
    float2 polar = float2(
           atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
           log(dot(i.uv, i.uv)) * 0.5f                       // log-radius
        );

    // Check how much our texture sampling point changes between
    // neighbouring pixels to the sides (ddx) and above/below (ddy)
    float4 gradient = float4(ddx(polar), ddy(polar));

    // If our angle wraps around between adjacent samples,
    // discard one full rotation from its value and keep the fraction.
    gradient.xz = frac(gradient.xz + 1.5f) - 0.5f;

    // Copy the polar coordinates before we scale & shift them,
    // so we can scale & shift the tint texture independently.
    float2 tintUVs = polar * _TintTex_ST.xy;
    tintUVs += _ScrollSpeeds.zw * _Time.x;

    polar *= _MainTex_ST.xy;
    polar += _ScrollSpeeds.xy * _Time.x;

    // Sample with our custom gradients.
    fixed4 col = tex2Dgrad(_MainTex, polar, 
                           _MainTex_ST.xy * gradient.xy,
                           _MainTex_ST.xy * gradient.zw
                         );

    // Since our tint texture has its own scale,
    // its gradients also need to be scaled to match.
    col *= tex2Dgrad(_TintTex, tintUVs,
                          _TintTex_ST.xy * gradient.xy,
                          _TintTex_ST.xy * gradient.zw
                         );

    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}
DMGregory
sumber
2
Beberapa perbaikan kecil yang dapat dilakukan: 1) menggunakan kurva logaritmik untuk arah v membantu tekstur mempertahankan bentuknya saat bergulir ke luar (alih-alih menyempit ke titik di tengah, Anda hanya mendapatkan rantai tak terbatas salinan yang lebih kecil hingga mip mengoleskannya) 2) menghitung gradien tekstur Anda sendiri dan menggunakan tex2Dgradperbaikan artefak penyaringan di mana sudut membungkus dari -pi ke + pi (itu adalah garis tipis piksel kabur di sisi kiri). Inilah hasil yang disentuh dengan lebih banyak warna biru
DMGregory
Luar biasa, terima kasih untuk kerusakannya juga. Saya merasa Shader adalah sesuatu yang saya mungkin tidak pernah bisa menulis (terutama hal-hal seperti apa yang telah Anda lakukan untuk "kutub", hal-hal matematika tidak ada artinya bagi saya, karena saya hanya punya keterampilan matematika yang sangat dasar).
JacketPotatoeFan
1
Hal-hal seperti itu biasanya akan dilihat orang. ;) Saya hanya cukup melakukannya sehingga bergulir dari keyboard. Coba bermain-main dengan fungsi matematika yang berbeda dan lihat hasilnya - memiliki alat seperti ini untuk membantu saya memvisualisasikan apa yang matematika lakukan secara geometris adalah bagaimana saya berlatih di tempat pertama. (Tidak khusus dengan shader, tetapi visualiser WinAmp tua yang disebut WhiteCap ...) Bahkan ketika matematika shader salah, seringkali aneh untuk dilihat. : D
DMGregory