1.序列帧动画
最常见的纹理动画之一就是序列帧动画。序列帧动画的原理非常简单。它像放电影一样,依次播放一系列关键帧图像,当播放速度达到一定数值时,看起来就是一个连续的动画。它的有点在于灵活性很强。我们不需要进行任何物理计算就可以得到很好的动画效果。
Shader "Unlit/ImageSequenceAnimation"
{
Properties
{
//包含了所有关键帧图像的纹理。
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color Tint", Color) = (1,1,1,1)
//分别代表该图像在水平方向和竖直方向包含的关键帧图像的个数。
_HorizontalAmount("HorizontalAmount",Float) = 4
_VerticalAmount("VerticalAmount", Float) = 4
//控制序列帧动画播放速度
_Speed("Speed",Range(1,100)) = 30
}
SubShader
{
//由于序列帧图像通常包含透明通道,因此可以被当成是一个半透明对象。在这里我们使用半透明的“标配”来设置它的SubShader标签,即把Queue和RenderType设置成Transparent,把IgnoreProject设置为True。在Pass中,我们使用Blend命令来开启并设置混合模式,同时关闭深度写入。
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass
{
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _Speed;
float _HorizontalAmount;
float _VerticalAmount;
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
{
//前3行代码计算了行列数,其中使用了Unity的内置时间变量_Time。由于_Time.y就是自该场景加载后所经过的时间。我们首先把_Time.y和速度属性_Speed相乘来得到模拟的时间,并使用CG的floor函数对结果值取整数时间time。然后,我们使用time除以_HorizontalAmount的结果值的商来作为当前对应的行索引,除法结果的余数则是列索引。接下来,我们需要使用行列索引值来构建真正的采样坐标。由于序列帧图像包含了许多关键帧图像,这意味着采样坐标需要映射到每个关键帧图像的坐标范围内。我们可以首先把原纹理坐标i.uv按行数和列数进行等分,得到每个子图像的纹理坐标范围。然后,我们需要使用当前的行列数对上面的结果进行偏移,得到当前子图像的纹理坐标。需要注意的是,对竖直方向的坐标便宜需要使用减法,这是因为在Unity中纹理坐标竖直方向的顺序和序列帧纹理中的顺序是相反的。
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorizontalAmount);
float column = time - row * _VerticalAmount;
half2 uv = i.uv + half2(column, -row);
uv.x /= _HorizontalAmount;
uv.y /= _VerticalAmount;
fixed4 col = tex2D(_MainTex, uv);
col.rgb *= _Color;
return col;
}
ENDCG
}
}
}
滚动的背景
很多2D游戏都是用了不断滚动的北京来模拟游戏角色在场景中的穿梭,这些背景往往包含了多个层来模拟一种视差效果。而这些背景的实现往往就是利用了纹理动画。
Shader "Unlit/scrollingbg"
{
Properties
{
//第一层背景纹理
_MainTex ("Texture", 2D) = "white" {}
//第二层背景纹理
_DetailTex ("2nd Layer (RGB)",2D) = "white" {}
//水平滚动速度
_ScrollX ("Base layer Scroll Speed", Float) = 1.0
_Scroll2X ("2nd layer Scroll Speed", Float) = 1.0
//控制纹理整体亮度
_Multiplier ("Layer Multiplier", Float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
//首先进行顶点变换,把顶点从模型空间变换到裁剪空间中。然后,我们计算两层纹理背景的纹理坐标。为此,我们首先利用TRANSFORM_TRANSFORM_TEX来得到初始的纹理坐标。然后,利用_Time.y变量在水平方向对纹理坐标进行偏移,我们吧两张纹理坐标存储在同一个变量o.uv中,以减少占用的插值寄存器空间。
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex) + frac(float2(_ScrollX, 0.0) * _Time.y);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _DetailTex) + frac(float2(_Scroll2X, 0.0) * _Time.y);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//我们首先分别利用i.uv.xy和i.uv.zw对两张背景纹理进行采样。然后,使用第二层纹理的透明通道来混合两张纹理。最后,使用_Multiplier参数和输出颜色进行相乘,以调整背景亮度。
fixed4 firstLayer = tex2D(_MainTex, i.uv.xy);
fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);
fixed4 c = lerp(firstLayer,secondLayer,secondLayer.a);
c.rgb *= _Multiplier;
return c;
}
ENDCG
}
}
}
来源:CSDN
作者:songtianqi123
链接:https://blog.csdn.net/songtianqi123/article/details/103756685