动画效果往往都是把时间添加到变量中,跟随时间变化画面也随之变化,
纹理动画和图像滚动
序列帧动画:依次播放一些列关键帧图像,当速度达到一定数值时,看起来就是一个连续的动画,灵活性强。精髓在于我们在每个时刻计算该时刻正在播放关键帧的位置,对该关键帧进行纹理采样。序列帧图片在设置时 选择开启透明Alpha通道。通过_Time索引序列帧图像中的子图像。
图像滚动:通过_Time控制偏移量实现滚动
Shader "Unlit/1"
{
Properties
{
_Color("_Color",Color)=(1,1,1,1)
_MainTex ("Texture", 2D) = "white" {}
_HorizontalAmount("_HorizontalAmount",Float)=4
_VerticalAmount("_VerticalAmount",Float)=4
_Speed("_Speed",Range(0,256))=1
}
SubShader
{ //序列帧动画通常为透明纹理
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass
{
Tags{"LightMode"="ForwardBase"}
//序列帧图像通常包含透明通道,因此可以被当成是一个半透明对象。
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#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);
//floor向下取整 时间除以_HorizontalAmount结果作为行索引,
float row=floor(time / _HorizontalAmount);
float column=floor(time / _VerticalAmount);
//对竖直方向使用减法,unity中的纹理坐标是竖直方向的顺序(从下往上逐渐增大)和序列帧纹理中的顺序(播放顺序从上到下)是相反的
half2 uv=i.uv + half2(column,-row);
//将源uv按行列等分,得到每个子图像纹理的坐标范围。
uv.x /= _HorizontalAmount;
uv.y /= _VerticalAmount;
//对计算的结果进行采样
fixed4 c = tex2D(_MainTex, uv);
c.rgb *= _Color;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
Shader "Unlit/2"
{
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" }
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _DetailTex;
float4 _MainTex_ST;
float4 _DetailTex_ST;
float _ScrollX;
float _Scroll2X;
float _Multiplier;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
};
v2f vert (a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//两张纹理存储到一个插值寄存器中,
//_Time作为uv的偏移量
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
{
fixed4 main= tex2D(_MainTex,i.uv.xy);
fixed4 detail= tex2D(_DetailTex,i.uv.zw);
//使用detail的透明通道混合两张纹理
fixed4 c=lerp(main,detail,detail.a);
c.rgb*=_Multiplier;
return c;
}
ENDCG
}
}
FallBack "VertexLit"
}
顶点动画
顶点动画让模型动画起来。在模型空间计算
Shader "Unlit/3"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_Magnitude ("Distortion Magnitude", Float) = 1
_Frequency ("Distortion Frequency", Float) = 1
_InvWaveLength ("Distortion Inverse Wave Length", Float) = 10
_Speed ("Speed", Float) = 0.5
}
SubShader
{
//"DisableBatching"="True"
//是否取消批处理 在使用批处理时会出现问题。批处理会合并所有相关的模型,而这些模型各自的模型空间就会丢失。
Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True"}
Pass
{
Tags{"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
//关闭剔除,在每个面都能渲染。
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
//控制振幅
float _Magnitude;
//控制频率
float _Frequency;
//控制波长
float _InvWaveLength;
float _Speed;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v)
{
v2f o;
float4 offset;
offset.yzw=float3(0,0,0);
//对x方向进行位移
offset.x=sin(_Frequency*_Time.y+v.vertex.x*_InvWaveLength +v.vertex.y*_InvWaveLength+v.vertex.z*_InvWaveLength)*_Magnitude;
o.pos = UnityObjectToClipPos(v.vertex+offset);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv+=float2(0,_Time.y*_Speed);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col *= _Color;
return col;
}
ENDCG
}
}
}
顶点动画阴影:
要想阴影随着顶点变化而变化,需要是自定义实现LightMode=ShadowCaster pass
Shader "Unlit/3"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_Magnitude ("Distortion Magnitude", Float) = 1
_Frequency ("Distortion Frequency", Float) = 1
_InvWaveLength ("Distortion Inverse Wave Length", Float) = 10
_Speed ("Speed", Float) = 0.5
}
SubShader
{
//"DisableBatching"="True"
//是否取消批处理 在使用批处理时会出现问题。批处理会合并所有相关的模型,而这些模型各自的模型空间就会丢失。
Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True"}
Pass
{
Tags{"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
//关闭剔除,在每个面都能渲染。
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
//控制振幅
float _Magnitude;
//控制频率
float _Frequency;
//控制波长
float _InvWaveLength;
float _Speed;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v)
{
v2f o;
float4 offset;
offset.yzw=float3(0,0,0);
//对x方向进行位移
offset.x=sin(_Frequency*_Time.y+v.vertex.x*_InvWaveLength +v.vertex.y*_InvWaveLength+v.vertex.z*_InvWaveLength)*_Magnitude;
o.pos = UnityObjectToClipPos(v.vertex+offset);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv+=float2(0,_Time.y*_Speed);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col *= _Color;
return col;
}
ENDCG
}
Pass {
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//shadowCaster 指令保证获取阴影相关变量正确赋值
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
struct v2f {
//定义阴影投射需要的定义的变量
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v) {
v2f o;
float4 offset;
offset.yzw = float3(0.0, 0.0, 0.0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
v.vertex = v.vertex + offset;
//计算坐标的转换。
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
fixed4 frag(v2f i) : SV_Target {
//自动完成阴影投射的计算,结果输出到深度图和映射纹理中
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "VertexLit"
}
广告牌
广告牌技术会根据视角方向来旋转一个被纹理着色器的多边形,使得多边形看起来总是面对摄像机。渲染烟雾、云朵、闪光等。广告牌技术本质是构建旋转矩阵,表面法线、指向上的方向、指向右的方向,以及一个锚点,锚点在旋转过程中是固定不变的。通过叉积得到三个相互正交方向。
Shader "Unlit/4"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1
}
SubShader
{
//顶点动画化是关闭批处理 "DisableBatching"="True"
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
Pass
{
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
//调整方向
fixed _VerticalBillboarding;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v)
{
v2f o;
//原点作为锚点
float3 center = float3(0, 0, 0);
//得到模型空间下的视角
float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));
//通过视角和锚点得到法线
float3 normalDir = viewer - center;
//_VerticalBillboarding控制 y 方向的约束度 为1 法向方向固定为视角方向 为0 上方向固定为向(0,1,0)
normalDir.y =normalDir.y * _VerticalBillboarding;
normalDir = normalize(normalDir);
//防止法线和向上方向平行(叉积得到错误的结果)。
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
//根据法线得到上和右的方向,
float3 rightDir = normalize(cross(upDir, normalDir));
upDir = normalize(cross(normalDir, rightDir));
//相对于锚点的偏移 以及三个正交基矢量得到最新的顶点位置。
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
o.pos = UnityObjectToClipPos(float4(localPos, 1));
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 c = tex2D (_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
注意,在顶点动画时,批处理会破环这种动画效果,需要使用DisableBatching=True 强制取消对shader的批处理,相应的会带来性能下降的问题以及增加Draw call的调用。应该尽量避免使用模型空间下的绝对位置和方向来计算。