UnityShader入门精要——纹理动画(1)

Unity中的内置时间变量

名称 类型 描述
_Time float4 t是自该场景加载开始所经过的时间,4个分量的值分别是(t/20, t, 2t 3t)。
_SinTime float4 t是时间的正弦值,4个分量的值分别是(t/8, t/4, t/2, t)
_CosTime float4 t是时间的余弦值,4个分量的值分别是(t/8, t4, t/2, t)
unity_DeltaTime float4 dt是时间增量,4个分量的值分别是(dt, 1/dt, smoothDt, 1/smoothDt)

序列帧动画

最常见的纹理动画之一就是序列帧动画。序列帧动画的原理非常简单,它像放电影一-样,依次播放一系列关键帧图像,当播放速度达到一定数值时,看起来就是一个连续的动画。它的优点在于灵活性很强,我们不需要进行任何物理计算就可以得到非常细腻的动画效果。而它的缺点也很明显,由于序列帧中每张关键帧图像都不一样,因此,要制作一张出色的序列帧纹理所需要的美术工程量也比较大。

Shader "Unity Shaders Book/Chapter 11/Image Sequence Animation" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Image Sequence", 2D) = "white" {}    //包含了所有关键帧图像的纹理
    	_HorizontalAmount ("Horizontal Amount", Float) = 4    //水平方向关键帧图像个数
    	_VerticalAmount ("Vertical Amount", Float) = 4    //竖直方向关键帧图像个数
    	_Speed ("Speed", Range(1, 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"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _HorizontalAmount;
			float _VerticalAmount;
			float _Speed;
			  
			struct a2v {  
			    float4 vertex : POSITION; 
			    float2 texcoord : TEXCOORD0;
			};  
			
			struct v2f {  
			    float4 pos : SV_POSITION;
			    float2 uv : TEXCOORD0;
			};  
			
			v2f vert (a2v v) {  
				v2f o;  
				o.pos = UnityObjectToClipPos(v.vertex);  
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);  
				return o;
			}  
			
			fixed4 frag (v2f i) : SV_Target {
				float time = floor(_Time.y * _Speed);  //模拟的时间
				float row = floor(time / _HorizontalAmount);    //行索引
				float column = time - row * _HorizontalAmount;    //列索引
				
				half2 uv = i.uv + half2(column, -row);
				uv.x /=  _HorizontalAmount;
				uv.y /= _VerticalAmount;
				
				fixed4 c = tex2D(_MainTex, uv);
				c.rgb *= _Color;
				
				return c;
			}
			
			ENDCG
		}  
	}
	FallBack "Transparent/VertexLit"
}

由于序列帧图像通常包含了透明通道,因此可以被当成是一个半透明对象。在这里我们使用半透明的“标配”来设置它的SubShader标签,即把Queue和RenderType设置成Transparent,把IgnoreProjector设置为True。在Pass中,我们使用Blend 命令来开启并设置混合模式,同时关闭了深度写入。

要播放帧动画,从本质来说,我们需要计算出每个时刻需要播放的关键帧在纹理中的位置。而由于序列帧纹理都是按行按列排列的,因此这个位置可以认为是该关键帧所在的行列索引数。因此,在上面的代码的前3行中我们计算了行列数,其中使用了Unity的内置时间变量_Time。 _Time.y 就是自该场景加载后所经过的时间我们首先把_Time.y 和速度属性_Speed 相乘来得到模拟的时间,并使用CG的floor函数对结果值取整来得到整数时间time。然后,我们使用time除以HorizontalAmount 的结果值的商来作为当前对应的行索引,除法结果的余数则是列索引。接下来,我们需要使用行列索引值来构建真正的采样坐标。由于序列帧图像包含了许多关键帧图像,这意味着采样坐标需要映射到每个关键帧图像的坐标范围内。我们可以首先把原纹理坐标i.uv按行数和列数进行等分,得到每个子图像的纹理坐标范围。然后,我们需要使用当前的行列数对上面的结果进行偏移,得到当前子图像的纹理坐标。需要注意的是,对竖直方向的坐标偏移需要使用减法,这是因为在Unity中纹理坐标竖直方向的顺序(从下到上逐渐增大)和序列帧纹理中的顺序(播放顺序是从上到下)是相反的。这样,我们就得到了真正的纹理采样坐标。

如何用Unity实现序列帧动画:

为序列帧素材更改Texture Type为Sprite模式,Sprite Mode 更改为Multiple(否则无法更改切片), Filter Mode 更改为 Point ,Compression更改为 None(无压缩),点击Apply。

  

在PackageManager中添加2D Sprite,点击Sprite Editor, 设置slice(根据素材大小设置每一块切分的大小)。

但此方法应用前提为导入的是一张GIF素材,切割后点击运行可以形成序列帧动画。

猜你喜欢

转载自blog.csdn.net/weixin_51327051/article/details/122950874