文章內使用Unity 2019 LTS
在今天的Unity(2020.09.27),我們有很多種方法可以製作動畫,對於我來說,組成動畫要素就是位移、縮放、旋轉,以及時間
Unity Shader系列文今天要來介紹如何利用Shader來製作簡單的2D動畫!
verter shader控制的就是模型中各個頂點位置,範例的話,可以參考本人在Day12寫看板功能,利用矩陣,也是最底層的方式,去完成位移、旋轉、縮放,也就是說,我們還缺了時間。
Unity Shader提供了四種跟時間相關的變數:
_Time
: 場景載入開始經過的時間_SinTime
: 時間的正弦值_CosTime
: 時間的餘弦值_unity_DeltaTime
: 時間之間的差量本篇文章只會用到_Time
。
仔細觀察的話,會發現有許多2D的背景圖不是單純的一張圖,而是至少有兩張不同的圖層的圖,以不同的速率,在循環撥放。
這裡是我所使用的素材:
使用裡面的far-background.png跟front-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
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
}
}
}
以下是結果: