纹理动画
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/Ani"
{
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",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
fixed4 _Color;
sampler2D _MainTex;
fixed4 _MainTex_ST;
float _HorizontalAmount;
float _VerticalAmount;
float _Speed;
struct a2v
{
fixed4 vertex : POSITION;
fixed3 normal : NORMAL;
fixed2 texcoord :TEXCOORD0;
};
struct v2f
{
fixed4 pos : SV_POSITION;
fixed2 uv : TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//_Time.y是自场景开始所经过的时间t
//floor得到整数的时间t
//举个例子现在打开了项目12秒
float time = floor(_Time.y * _Speed);
//除法取整作为行索引值
//12 / 4 取整 = 3 ,索引值3
float row = floor(time / _HorizontalAmount);
//除法结果的余数作为列索引值
//12 - 3*4 = 0 索引值0
float column = time - row * _HorizontalAmount;
//转换成uv坐标
half2 uv = float2(i.uv.x / _HorizontalAmount,i.uv.y / _VerticalAmount);
uv.x += column / _HorizontalAmount;
uv.y -= row / _VerticalAmount;
//采样
fixed4 c = tex2D(_MainTex,uv);
c.rgb *= _Color;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
动态背景
Shader "Custom/background"
{
Properties
{
_MainTex("Base Layer",2D) = "white"{}
_DetailTex("2nd Layer",2D) = "white"{}
_ScrollX("Base Layer Speed",Float) = 1.0
_Scroll2X("2nd Layer Speed",Float) = 1.0
_Multiplier("Layer Multiplier",Float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _DetailTex;
float4 _DetailTex_ST;
float _ScrollX;
float _Scroll2X;
float _Multiplier;
struct a2v
{
fixed4 vertex : POSITION;
fixed3 normal : NORMAL;
fixed4 texcoord : TEXCOORD0;
};
struct v2f
{
fixed4 pos : SV_POSITION;
fixed4 uv : TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//frac函数返回标量或每个矢量中各分量的小数部分
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw + frac(float2(_ScrollX, 0.0) * _Time.y);
o.uv.zw = v.texcoord.xy * _DetailTex_ST.xy + _DetailTex_ST.zw + frac(float2(_Scroll2X, 0.0) * _Time.y);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 firstLayer = tex2D(_MainTex,i.uv.xy);
fixed4 secondLayer = tex2D(_MainTex,i.uv.zw);
fixed4 c = lerp(firstLayer, secondLayer,secondLayer.a);
c.rgb *= _Multiplier;
return c;
}
ENDCG
}
}
FallBack "VertexLit"
}
frac(float2(0 , y) * _Time.y)取小数使偏移量在(0,1)之间
uv分量取值在(0,1)
2D河流顶点动画
Shader "Custom/water"
{
Properties
{
_MainTex("Main Tex",2D) = "white"{}
_Color("Color",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
{
//标记不进行批处理,因为顶点动画需要对顶点进行移动
//批处理会把模型合并
Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparnet" "DisableBatching" = "true"}
pass
{
Tags{"LightMode" = "ForwardBase"}
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
Cull off
CGPROGRAM
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
//幅度
float _Magnitude;
//波动频率
float _Frequency;
//波长的倒数
float _InvWaveLength;
//河流纹理移动的速度
float _Speed;
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
fixed4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
fixed4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
float4 offset;
offset.yzw = float3(0,0,0);
//计算顶点偏移量,只希望x方向偏移
//实际上是进行了一次 a * sin(_Frequency * x + y) + x 的变化,x在正弦函数下上下浮动
//上面的y == v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength
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.0,_Time.y * _Speed);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 c= tex2D(_MainTex,i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
注意取消批处理
顶点实际上是在A * sin(ax + y)上偏移
如果要使顶点动画有变换后的形态的阴影,需要新增一个Pass
广告牌效果
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
Shader "Custom/star"
{
Properties
{
_MainTex("Main Tex",2D) = "white"{}
_VerticalBillboarding("Vertical Restraints",Range(0,1)) = 1
}
SubShader
{
Tags{"RenderType" = "Transparent" "Queue"= "Transparent" "IgnoreProjector" = "True" "DisableBatching" = "True"}
Pass
{
Tags{"LightMode" = "ForwardBase"}
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
Cull off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
float4 _MainTex_ST;
float _VerticalBillboarding;
struct a2v
{
fixed4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
fixed4 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;
normalDir.y = normalDir.y * _VerticalBillboarding;
//当_VerticalBillboarding为0时,法线方向固定为(0,1,0)
//当_VerticalBillboarding为1时,法线方向始终向着照相机
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(rightDir,normalDir));
//获取原始位置相对于锚点的偏移值
float3 centerOffs = v.vertex.xyz - center;
//
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
o.pos = UnityObjectToClipPos(float4(localPos,1));
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex,i.uv);
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
view - center使法线方向始终向着相机
向上方向upDir取(0,1,0),当法线y值也为0时,也就是摄像机在物体正上方时,upDir改为(0,0,1)防止平行
叉乘得到rightDir,rightDir和法线方向再叉乘得到真正的坐标系
计算新顶点坐标公式:
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
我也不知道为什么这么算
构建出坐标系计算偏移后的顶点坐标再传出去