使用纹理,能够迅速让 Shader 取得非常好的效果
同时,也需要注意纹理的数量,因为它会给系统带来较大的负担,尤其是在手机上需要将纹理控制在最小,这样才能保证游戏运行的流畅性
有关于二维纹理如何映射到三维表面,参考这里
http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter03.html
UV 滚轴动画
使用 UV 动画来实现让贴图随着时间沿着 u,v 两个方向移动
它的实现原理就是,用Unity为我们提供的系统时间乘以自定义的速度属性,作为 UV 坐标的索引值从贴图中取值,实现 UV 滚轴动画。
UV 滚轴动画的原理图解
我们需要先在属性段里添加两个float属性,分别用于控制 UV 动画两个方向的速度
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_ScrollXSpeed ("X Scroll Speed", Range(0, 10)) = 2
_ScrollYSpeed ("Y Scroll Speed", Range(0, 10)) = 2
}
在SubShader块内的 CGPROGRAM 内再次声明这两个变量,
在 #pragma surface surf Lambert 后添加
fixed _ScrollXSpeed;
fixed _ScrollYSpeed;
再修改 surface 函数,将传递给 tex2D函数(用于进行纹理采样)的UV值随着时间进行变换。
我们使用内建的 _Time 变量来实现 UV 动画,当编辑器中 Play 按钮被按下时,_Time 会开始走动。
对于更多的内建参数,参考
http://docs.unity3d.com/Documentation/Components/SL-BuiltinValues.html
将 surface 函数修改为
void surf (Input IN, inout SurfaceOutput o)
{
//创建一个变量存储当前输入的UV值
fixed2 scrolledUV = IN.uv_MainTex;
//分别存储x, y 两个方向的速度
fixed xScrollValue = _ScrollXSpeed * _Time;
fixed yScrollValue = _ScrollYSpeed * _Time;
//将 UV 偏移应用于之前建立的变量
scrolledUV += fixed2(xScrollValue, yScrollValue);
//应用动画化的UV值从纹理中获取颜色值,并传递给output
half4 c = tex2D (_MainTex, scrolledUV);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
最终实现的动画效果为
最终的 UV 动画 Shader 为
Shader "HineNotes/CookbookCh_02/AnimatedUV" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_ScrollXSpeed ("X Scroll Speed", Range(0, 10)) = 2
_ScrollYSpeed ("Y Scroll Speed", Range(0, 10)) = 2
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
fixed _ScrollXSpeed;
fixed _ScrollYSpeed;
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed2 scrolledUV = IN.uv_MainTex;
fixed xScrollValue = _ScrollXSpeed * _Time;
fixed yScrollValue = _ScrollYSpeed * _Time;
scrolledUV += fixed2(xScrollValue, yScrollValue);
half4 c = tex2D (_MainTex, scrolledUV);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
UV Sprite Sheets 动画
UV Sprite Sheets 动画方式,实际是将许多大小相等的序列帧以矩阵方式排列到一张贴图上,shader 按照时间顺序逐帧读取对应的像素块并播放出来,形成了序列帧动画。
UV Sprite Sheets 要怎么翻译呢? UV 精灵表单?—呵呵,好调皮的名字啊 = =b 所以还是用英文吧
如下图,是我们要用到的二维 UV Sprite Sheets
UV Sprite Sheets 原理图解
注:以下的实现方式与原书中略有不同,去掉了不必要的参数,并将读取的 sheet 从一维扩展到了二维
在 Shader 的属性段中添加属性
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_CellAmountRow ("Cell Amount Row", float) = 0.0 // 单元格行数
_CellAmountCol ("Cell Amount Column", float) = 0.0 // 单元格列数
_Speed ("Speed", Range( 0.01, 32 )) = 12 // 播放速度
}
再修改 Surface 函数
// 先将输入的 UV 值存储起来
float2 spriteUV = IN.uv_MainTex;
根据单元格的行列数目来计算得到单个单元格在横向和纵向的百分比
float cellUVPercentageX = 1/_CellAmountCol;
float cellUVPercentageY = 1/_CellAmountRow;
行乘以列,得到总共的单元数目
float cellAmount = _CellAmountRow * _CellAmountCol;
利用 Unity Shader 的内建变量 _Time 来得到一个随着时间递增的值
float timeVal = fmod ( _Time.y * _Speed, _CellAmount);
timeVal = ceil (timeVal);
计算 x 方向的偏移值
spriteUV.x += int(fmod(timeVal,_CellAmountCol));
计算 y 方向的偏移值
spriteUV.y += int(timeVal/_CellAmountCol);
最后乘以百分比将其缩放
spriteUV.x *= cellUVPercentageX;
spriteUV.y *= cellUVPercentageY;
传递最终输出值
half4 c = tex2D (_MainTex, spriteUV);
o.Albedo = c.rgb;
o.Alpha = c.a;
使用 CGFX 中的内建函数 fmod()
函数 | 描述 |
---|---|
fmod(x,y) | 返回 x / y 的余数,与 x 同号。如果 y 为 0, 结果由解释器定义决定。 |
使用 CGFX 中的内建函数 ceil()
函数 | 描述 |
---|---|
ceil(x) | 返回不小于 x 的最大整数 |
最终实现的动画效果为
最终的 UV Sprite Sheet Shader 代码为
Shader "HineNotes/CookbookCh_02/UVSpreiteSheet"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
//Create the properties below
_CellAmountRow ("Cell Amount Row", float) = 0.0
_CellAmountCol ("Cell Amount Column", float) = 0.0
_Speed ("Speed", Range(0.01, 32)) = 12
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
//Create the connection to the properties inside of the
//CG program
float _CellAmountRow;
float _CellAmountCol;
float _Speed;
struct Input
{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o)
{
// store UVs in a seperate variable
float2 spriteUV = IN.uv_MainTex;
// calculate percentage of x
float cellUVPercentageX = 1/_CellAmountCol;
// calculate percentage of y
float cellUVPercentageY = 1/_CellAmountRow;
// calculate the amount of cells
float cellAmount = _CellAmountRow * _CellAmountCol;
// get a stair step value out of time so we can increment
// the uv offset
float timeVal = fmod(_Time.y * _Speed, cellAmount);
timeVal = ceil(timeVal);
// calculate x offset
spriteUV.x += int(fmod(timeVal,_CellAmountCol));
// calculate y offset
spriteUV.y += int(timeVal/_CellAmountCol);
// scale it to cell
spriteUV.x *= cellUVPercentageX;
spriteUV.y *= cellUVPercentageY;
half4 c = tex2D (_MainTex, spriteUV);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}