Unity Shader - 翻書效果

今天實現一個簡單的翻書的效果,話不多說,先上一張效果圖:

這裏就隨便用的一張紋理了,我們還是稱爲“翻木板”吧,哈哈。
在這裏插入圖片描述

實現過程:

其實這個效果實現起來還是挺簡單的,大概思路其實就是 讓所有頂點都繞Z軸旋轉,並且通過正餘弦使之帶有一點弧度。

下面開始讓我們一步一步的實現該效果。

首先打開Unity新建一個工程,場景,並且創建一個名爲openBookEffect的Shader文件,刪掉原本多餘的代碼。

第一步,我們先讓它繞z軸旋轉起來

這裏就要用到一個旋轉矩陣了,讓頂點左乘該矩陣,就能得到旋轉之後的位置了。(ps:這裏就不詳細的解釋旋轉矩陣怎麼推導來的了,有興趣的可以去百度瞭解一下。)

旋轉矩陣有3種:

  1. 繞x軸旋轉:
    在這裏插入圖片描述
  2. 繞y軸旋轉
    在這裏插入圖片描述
  3. 繞z軸旋轉
    在這裏插入圖片描述

很明顯,我們這裏需要用到的是第三個 繞z軸旋轉的矩陣。下面我們通過代碼來構建一個旋轉矩陣並使之旋轉一定的角度:

Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        //旋轉角度
        _Angle("Angle",Range(0,180))=0
    }
 	....
    sampler2D _MainTex;
    //角度
    float _Angle;
	//頂點着色器
    v2f vert (appdata v)
    {
        v2f o;
        float s;
        float c;
        //通過該方法可以計算出該角度的正餘弦值
		sincos(radians(_Angle),s,c);
        //旋轉矩陣
        float4x4 rotateMatrix={			
            c ,s,0,0,
            -s,c,0,0,
            0 ,0,1,0,
            0 ,0,0,1
        };
        //頂點左乘以旋轉矩陣
		v.vertex = mul(rotateMatrix,v.vertex);
		//模型空間轉換到裁剪空間
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        return o;
    }
    ....

修改 _Angle 大小,來旋轉平面,如圖:
在這裏插入圖片描述
通過測試發現,這樣的旋轉並不是我們想要的效果,此時旋轉的軸在中心,我們想讓它的旋轉軸在最左邊,此時就需要把所有頂點在旋轉之前都往左偏移5個單位旋轉完成之後再向右偏移5個單位就可以達到我們想要的效果了,代碼如下:

  v2f vert (appdata v)
            {
                v2f o;
                //旋轉之前向左偏移5個單位
				v.vertex -= float4(5,0,0,0);
                float s;
                float c;
                //通過該方法可以計算出該角度的正餘弦值
                sincos(radians(_Angle),s,c);
                //旋轉矩陣
                float4x4 rotateMatrix={
                    c ,s,0,0,
                    -s,c,0,0,
                    0 ,0,1,0,
                    0 ,0,0,1
                };
                //頂點左乘以旋轉矩陣
                v.vertex = mul(rotateMatrix,v.vertex);
                //旋轉之後偏移回來
				v.vertex += float4(5,0,0,0);

                //模型空間轉換到裁剪空間
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

在這裏插入圖片描述

現在有一點翻書的樣子了,但是現在的翻書效果太生硬了,爲了接近真實的翻書效果,我們就需要通過正餘弦函數修改頂點的y座標,來達到一個弧度的效果。

v2f vert (appdata v)
 {
     v2f o;
     //旋轉之前向右偏移5個單位
     v.vertex -= float4(5,0,0,0);
     float s;
     float c;
     //通過該方法可以計算出該角度的正餘弦值
     sincos(radians(_Angle),s,c);
     //旋轉矩陣
     float4x4 rotateMatrix={
         c ,s,0,0,
         -s,c,0,0,
         0 ,0,1,0,
         0 ,0,0,1
     };
     //根據x座標,通過正弦函數計算出 y座標的正弦值,  _WaveLength 控制波長, 振幅就跟隨角度正弦值動態變化
     v.vertex.y = sin(v.vertex.x*_WaveLength) * s ;

     //頂點左乘以旋轉矩陣
     v.vertex = mul(rotateMatrix,v.vertex);
     //旋轉之後偏移回來
     v.vertex += float4(5,0,0,0);

     //模型空間轉換到裁剪空間
     o.vertex = UnityObjectToClipPos(v.vertex);
     o.uv = v.uv;
     return o;
 }

效果如下:
在這裏插入圖片描述
在這裏插入圖片描述

現在看着效果是不是闊以了。感覺效果還挺不錯的,但是還沒完,我們仔細觀察會發現“翻書”的過程,背面有點不真實,不應該是該紋理的反面,而是另一張新的紋理,此時我們該怎麼辦呢?
其實很簡單,只需要把正面和反面分開渲染就可以了,一個Pass渲染正面,一個Pass渲染背面。

首先我們需要通過 Cull指令剔除不需要渲染的那一面。

完整代碼如下:

Shader "Learn Unity Shader/openBook"
{
    Properties
    {
        //正面紋理
        _MainTex ("Texture", 2D) = "white" {}
        //背面紋理
		_SecTex("SecTex",2D)="White"{}

        //旋轉角度
        _Angle("Angle",Range(0,180))=0
        //波長
        _WaveLength("WaveLength",Range(-1,1))=0

    }
    SubShader
    {

        Pass
        {
            //剔除背面
			Cull Back

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
			float4 _MainTex_ST;
            //角度
            float _Angle;
            //波長
            float _WaveLength;

            v2f vert (appdata v)
            {
                v2f o;
                //旋轉之前向右偏移5個單位
                v.vertex -= float4(5,0,0,0);
                float s;
                float c;
                //通過該方法可以計算出該角度的正餘弦值
                sincos(radians(_Angle),s,c);
                //旋轉矩陣
                float4x4 rotateMatrix={
                    c ,s,0,0,
                    -s,c,0,0,
                    0 ,0,1,0,
                    0 ,0,0,1
                };
                //根據x座標,通過正弦函數計算出 y座標的正弦值,  _WaveLength 控制波長, 振幅就跟隨角度正弦值動態變化
                v.vertex.y = sin(v.vertex.x*_WaveLength) * s ;

                //頂點左乘以旋轉矩陣
                v.vertex = mul(rotateMatrix,v.vertex);
                //旋轉之後偏移回來
                v.vertex += float4(5,0,0,0);

                //模型空間轉換到裁剪空間
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }


            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }

         Pass
        {
            //剔除正面
			Cull Front

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            //角度
            float _Angle;
            //波長
            float _WaveLength;

            sampler2D _SecTex;
			float4 _SecTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                //旋轉之前向右偏移5個單位
                v.vertex -= float4(5,0,0,0);
                float s;
                float c;
                //通過該方法可以計算出該角度的正餘弦值
                sincos(radians(_Angle),s,c);
                //旋轉矩陣
                float4x4 rotateMatrix={
                    c ,s,0,0,
                    -s,c,0,0,
                    0 ,0,1,0,
                    0 ,0,0,1
                };
                //根據x座標,通過正弦函數計算出 y座標的正弦值,  _WaveLength 控制波長, 振幅就跟隨角度正弦值動態變化
                v.vertex.y = sin(v.vertex.x*_WaveLength) * s ;

                //頂點左乘以旋轉矩陣
                v.vertex = mul(rotateMatrix,v.vertex);
                //旋轉之後偏移回來
                v.vertex += float4(5,0,0,0);

                //模型空間轉換到裁剪空間
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }


            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_SecTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

最終效果:
在這裏插入圖片描述
參數參考
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章