iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 13
0
自我挑戰組

初見Unity Shader系列 第 13

時間變數與2D動畫

文章內使用Unity 2019 LTS

在今天的Unity(2020.09.27),我們有很多種方法可以製作動畫,對於我來說,組成動畫要素就是位移、縮放、旋轉,以及時間

Unity Shader系列文今天要來介紹如何利用Shader來製作簡單的2D動畫!

目標

  • 無限捲動背景(Shader Version)
  • 圖像序列動畫(Shader Version)

Unity Shader的時間變數

verter shader控制的就是模型中各個頂點位置,範例的話,可以參考本人在Day12寫看板功能,利用矩陣,也是最底層的方式,去完成位移、旋轉、縮放,也就是說,我們還缺了時間

Unity Shader提供了四種跟時間相關的變數:

  • _Time: 場景載入開始經過的時間
  • _SinTime: 時間的正弦值
  • _CosTime: 時間的餘弦值
  • _unity_DeltaTime: 時間之間的差量

本篇文章只會用到_Time

無限捲動背景

仔細觀察的話,會發現有許多2D的背景圖不是單純的一張圖,而是至少有兩張不同的圖層的圖,以不同的速率,在循環撥放。

開始撰寫!

這裡是我所使用的素材:

使用裡面的far-background.pngfront-background.png當作素材

Shader "Learning/InfinityScrollBg"
{
    Properties
    {
        _BackTex ("Back Layer", 2D) = "white" {}
        _FrontTex ("Front Layer", 2D) = "white" {}
        _BackScrollSpeed ("Back Layer Scroll Speed", Float) = 1.0
        _FrontScrollSpeed ("Front Layer Scroll Speed", Float) = 1.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }

        Pass
        {
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                // 把後景跟前景的紋理座標放在同一組座標變數中
                // 為了減少快取的使用
                float4 uv : TEXCOORD0;
            };

            sampler2D _BackTex;
            float4 _BackTex_ST;
            sampler2D _FrontTex;
            float4 _FrontTex_ST;
            float _BackScrollSpeed;
            float _FrontScrollSpeed;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                // 後面的 "frac(float2(_Speed, 0.0) * _Time.y)" 視為每秒位移的變化量
                o.uv.xy = TRANSFORM_TEX(v.uv, _BackTex) + frac(float2(_BackScrollSpeed, 0.0) * _Time.y);
                o.uv.zw = TRANSFORM_TEX(v.uv, _FrontTex) + frac(float2(_FrontScrollSpeed, 0.0) * _Time.y);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 firstLayer = tex2D(_BackTex, i.uv.xy);
                fixed4 secondLayer = tex2D(_FrontTex, i.uv.zw);

                // 利用lerp將2nd Layer跟1st Layer混合再一起
                fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);

                return c;
            }
            ENDCG
        }
    }
}

上面的範例使用的是_Time.y_Time為float4,存放四個變數,而y分量是沒有經過任何變化的時間分量

from Unity Answers

圖像序列動畫(Image Sequence Animation)

from Open Game Art

這是2D動畫一種我們熟悉也常見的作法,把每一幀的動作畫出來,然後利用時間取操控它的動作。

就我所知圖像序列動畫還有蠻多名字的,例如: Sprite Sheet Animation,但這些都是指同一種動畫作法。

Shader "Learning/ISA"
{
    Properties
    {
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Image Sequence", 2D) = "white"{}
        // 水平與垂直的數目,若拿範例圖這裡的數目是8x8
        _HorizontalAmount ("Horizontal Amount", Float) = 4
        _VerticalAmount ("Vertical Amount", Float) = 4
        _Speed ("Animation Speed", Range(0, 100)) = 30
    }
    SubShader
    {
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }

        Pass
        {
            Tags { "LightMode"="ForwardBase" }

            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            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;
            };

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _HorizontalAmount;
            float _VerticalAmount;
            float _Speed;

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

            fixed4 frag (v2f i) : SV_Target
            {
                float time = floor(_Time.y * _Speed);
                float row = floor(time / _HorizontalAmount);    // x index
                float column = time - row * _HorizontalAmount;  // y index

                // 負號是因為原圖是從左上到右下,texture coordinate是從左下到右上
                half2 uv = i.uv + half2(column, -row); // 根據時間更新座標
                
                uv.x /= _HorizontalAmount;
                uv.y /= _VerticalAmount;

                fixed4 c = tex2D(_MainTex, uv);
                c.rgb *= _Color;
                return c;
            }
            ENDCG
        }
    }
}

以下是結果:

Reference


上一篇
看板、告示牌、Billboarding都是同一個東西
下一篇
半透明效果
系列文
初見Unity Shader30

尚未有邦友留言

立即登入留言