Shader實例:扭曲,漩渦


效果:

案例:新仙劍,王者之劍。
在切換場景的時候,就會有這樣的全屏扭曲效果。

思路:
1.用GrabPass抓屏到一張紋理中。
2.進行扭曲,繪制到UGUI的Image上。

准備:
去官網下載Unity內置Shader,當前最新版本:builtin_shaders-5.3.1f1
http://unity3d.com/cn/get-unity/download/archive
里面有Image用的默認Shader:Sprites-Default
我們要在這個shader的基礎上加上扭曲效果。

GrabPass比較耗,加上我沒有好看的場景,這里就用一張全屏的背景圖進行扭曲了。
代碼中//–add 就是在原來基礎上添加部分

Shader "Custom/ImageSwirl"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
    }
 
    SubShader
    {
        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
            "PreviewType" = "Plane"
            "CanUseSpriteAtlas" = "True"
        }
 
        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha
 
 
        //-----add
        GrabPass
        {
            "_MyGrabTex"
        }
        //---------
 
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ PIXELSNAP_ON
            #include "UnityCG.cginc"
 
            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };
 
            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color : COLOR;
                float2 texcoord  : TEXCOORD0;
            };
 
            fixed4 _Color;
 
            //------------add
            float _Radius;
            float _Angle;
            sampler2D _MyGrabTex;
            float2 swirl(float2 uv);
            float2 swirl(float2 uv)
            {
                //先減去貼圖中心點的紋理坐標,這樣是方便旋轉計算 
                uv -= float2(0.5, 0.5);
 
                //計算當前坐標與中心點的距離。 
                float dist = length(uv);
 
                //計算出旋轉的百分比 
                float percent = (_Radius - dist) / _Radius;
 
                if (percent < 1.0 && percent >= 0.0)
                {
                    //通過sin,cos來計算出旋轉后的位置。 
                    float theta = percent * percent * _Angle * 8.0;
                    float s = sin(theta);
                    float c = cos(theta);
                    //uv = float2(dot(uv, float2(c, -s)), dot(uv, float2(s, c))); 
                    uv = float2(uv.x*c - uv.y*s, uv.x*s + uv.y*c);
                }
 
                //再加上貼圖中心點的紋理坐標,這樣才正確。 
                uv += float2(0.5, 0.5);
 
                return uv;
            }
            //---------------
 
            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap(OUT.vertex);
                #endif
 
                return OUT;
            }
 
            sampler2D _MainTex;
            sampler2D _AlphaTex;
            float _AlphaSplitEnabled;
 
            fixed4 SampleSpriteTexture(float2 uv)
            {
                fixed4 color = tex2D(_MainTex, uv);
 
                //----------modify
                //fixed4 color = tex2D(_MyGrabTex, float2(uv.x,1-uv.y));
                //-----------
 
 
        #if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
                if (_AlphaSplitEnabled)
                    color.a = tex2D(_AlphaTex, uv).r;
        #endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
 
                return color;
            }
 
            fixed4 frag(v2f IN) : SV_Target
            {
                //---add
                IN.texcoord = swirl(IN.texcoord);
                //--------
 
                fixed4 c = SampleSpriteTexture(IN.texcoord) * IN.color;
                c.rgb *= c.a;
                return c;
            }
            ENDCG
        }
    }
}

核心:

//uv = float2(dot(uv, float2(c, -s)), dot(uv, float2(s, c))); 
uv = float2(uv.x*c - uv.y*s, uv.x*s + uv.y*c);

我的另一篇文章:基礎知識:Q&A 中有對旋轉矩陣進行簡單推導。

C#腳本:以時間驅動來增長角度和半徑

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
 
public class Swirl : MonoBehaviour
{
    float angle = 0;
    float radius = 0.1f;
    Material mat;
    void Start () 
    {
            mat = this.GetComponent<Image>().material;
 
            //延遲2秒開始,每隔0.2s調用一次
            InvokeRepeating("DoSwirl", 2f, 0.2f);
    }
 
    void DoSwirl()
    {
        angle += 1f;
        radius += 0.1f;
 
        mat.SetFloat("_Angle",angle);
        mat.SetFloat("_Radius",radius);
 
           //rest
           if (radius >= 0.6f)
           {
            angle = 0;
            radius = 0.1f;
           }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM