Unity Shader UV动画(序列帧动画)

时间:2021-10-03 03:28:06

这一节我们用下面的图片:

Unity Shader UV动画(序列帧动画)

来实现动态的熊熊的烈火效果:

Unity Shader UV动画(序列帧动画)


我们先来看一下全部的Shader代码,然后再进行分析

Shader "Custom/UVAnim"
{
Properties
{
_Color("Base Color", Color) = (1,1,1,1)
_MainTex("Base(RGB)", 2D) = "white" {}
}

SubShader
{
tags{"Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"}
Blend SrcAlpha OneMinusSrcAlpha

Pass
{

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

float4 _Color;
sampler2D _MainTex;

struct v2f
{
float4 pos:POSITION;
float2 uv:TEXCOORD0;
};

float2 moveUV(float2 vertUV)
{
float textureNum = 12.0;
float timePerFrame = 100;

float index = frac(_Time.x / textureNum * timePerFrame);
float2 uvScale = float2(1 / textureNum, 1);

if(index <= uvScale.x)
return vertUV * uvScale;
else if(index <= 2 * uvScale.x)
return vertUV * uvScale + float2(uvScale.x, 0.0);
else if(index <= 3 * uvScale.x)
return vertUV * uvScale + float2(2 * uvScale.x, 0.0);
else if(index <= 4 * uvScale.x)
return vertUV * uvScale + float2(3 * uvScale.x, 0.0);
else if(index <= 5 * uvScale.x)
return vertUV * uvScale + float2(4 * uvScale.x, 0.0);
else if(index <= 6 * uvScale.x)
return vertUV * uvScale + float2(5 * uvScale.x, 0.0);
else if(index <= 7 * uvScale.x)
return vertUV * uvScale + float2(6 * uvScale.x, 0.0);
else if(index <= 8 * uvScale.x)
return vertUV * uvScale + float2(7 * uvScale.x, 0.0);
else if(index <= 9 * uvScale.x)
return vertUV * uvScale + float2(8 * uvScale.x, 0.0);
else if(index <= 10 * uvScale.x)
return vertUV * uvScale + float2(9 * uvScale.x, 0.0);
else if(index <= 11 * uvScale.x)
return vertUV * uvScale + float2(10 * uvScale.x, 0.0);
else
return vertUV * uvScale + float2(11 * uvScale.x, 0.0);
}

v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = moveUV(v.texcoord.xy);
return o;
}

half4 frag(v2f i):COLOR
{
half4 c = tex2D(_MainTex , i.uv) * _Color;
return c;
}

ENDCG
}
}
}

首先属性部分Properties和Tags标签我们前面已经讲过了,然后是混合Blend

Blend SrcAlpha OneMinusSrcAlpha 这句话的意思是拿 源颜色和目标颜色进行混合

关于混合就是:

color = (RGBsrc * Ksrc) OP (RGBdst * Kdst)

其中color表示alpha混合后的颜色值,RGBsrc表示源颜色值,即将要绘制的图元的颜色值;Ksrc表示源混合系数,通常赋值为表示半透明程度的alpha值,用来和RGBsrc相乘。RGBdst表示目标颜色值,即当前颜色缓冲区中的颜色值,Kdst表示目标混合系数,用来和RGBdst相乘。OP表示源计算结果与颜色缓冲区计算结果的混合方法即源计算结果与颜色缓冲区计算结果相加,至于混合的方法OP和混合系数Ksrc和Kdst请参阅unity文档,有很多种。


我们的顶点着色器vert和片段着色器函数frag中的代码很好理解。不好理解的就是我们去uv坐标的moveUV函数。

moveUV函数要求传入一个float2类型的uv值,然后我们定义了两个变量

float textureNum = 12.0;
float timePerFrame = 100;

为什么我们定义textureNum为12,因为我们的图片上有12个小火的形象,我们会分别取他们。

我们来看看我们是怎么来用他们的

 

float index = frac(_Time.x / textureNum * timePerFrame);

关于_Time unity文档中是这样解释的

Unity Shader UV动画(序列帧动画) 

frac函数返回一个数据的小数部分那么_Time.x / textureNum就可以返回1/12-1之间的数值,乘以timePerFrame是控制0-11之间的变换速度的。

也就是我们的index值在0-11之间。

下面这句话很简单,就不解释了。

float2 uvScale = float2(1 / textureNum, 1);

我们来解释这三句,其他的都是一样。

if(index <= uvScale.x)
return vertUV * uvScale;
else if(index <= 2 * uvScale.x)
return vertUV * uvScale + float2(uvScale.x, 0.0);
else if(index <= 3 * uvScale.x)
return vertUV * uvScale + float2(2 * uvScale.x, 0.0);

如果index小于 1/12时,我们把UV坐标乘以1/12

return vertUV * uvScale;

其实我们取的是纹理图片的第一个火的纹理。

当index小于2/12时,我们把UV坐标乘以1/12再加上float2(uvScale.x, 0.0)

return vertUV * uvScale + float2(uvScale.x, 0.0);

我们先取到第一个火纹理,然后再加1/12取到第二个火纹理.,最后这句话就是获取第二个火的纹理。

当index小于2/12时,我们把UV坐标乘以1/12再加上float2(2 * uvScale.x, 0.0)

return vertUV * uvScale + float2(2 * uvScale.x, 0.0);

我们先取到第一个火纹理,然后再加2/12取到第三个火纹理.,最后这句话就是获取第三个火的纹理。


这样随着时间的流逝我们会显示不同的纹理,然后进行放大(UV坐标乘以一个数,实际上就是把源纹理放大了),就有了动态火的效果。