——20.9.14app
Shader中主要有及两种动画,一种就是纹理动画还有一种就是顶点动画。函数
动画效果通常都须要把时间加入一些变量的计算,以便画面能够随时间发生变化。下面是Shader中的如何去访问时间的方法。性能
1、纹理动画动画
序列帧动画就是咱们接触的第一种纹理动画。序列帧动画原理就是依次播放一系列关键帧动画。当图片的切换速度达到必定数值后,看上去就像是连续的动画。优势:在于它的灵活性强,不用进行物理计算就会有对应的动画效果。缺点:依旧是须要逐关键帧的动画内容,工做量依旧很大。spa
咱们看看shaderlab部分。咱们须要看有什么样的变量。通常来讲序列帧通常是一张图片包括了全部的关键帧。而且按播放顺序排放。而且方便读取通常是规整的(就是没有边框)。因此咱们要肯定有几行_HorizontalAmount几列_VerticalAmount根据图片决定。还有就是播放速度_Speed,最后就是图片_MainTex,和颜色_Color。orm
//frag
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorizontalAmount);
float column = time - row * _VerticalAmount; half2 uv = float2(i.uv.x / _HorizontalAmount, i.uv.y / _VerticalAmount); uv.x += column / _HorizontalAmount; uv.y -= row / _VerticalAmount; //half2 uv = i.uv + half2(column, -row); //uv.x /= _HorizontalAmount; //uv.y /= _VerticalAmount;
首先是_Time.y就是取时间变量t,而后经过取整来肯定行数,而后经过取小数来肯定列数。这里是在片元着色器中的两种读取方式。一种是根据行和列把坐标放大到整张图片。而且经过增长基础单位来进行能够看到对uv.y进行的是减操做。是由于unity里面是从左下为(0,0)。而后第二种方法先去肯定要读取的行列。而后再去细分到一个区间内。要注意上面这两种方法本质上都是为了取这张关键帧中的一张图片。因而就要把uv坐标缩放到其中的一张。blog
Shader "Unlit/11-1ImageSequenceAnimation" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Texture", 2D) = "white" {} _HorizontalAmount ("HorizontalAmount", Float) = 4 _VerticalAmount ("VerticalAmount", 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 multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; float _VerticalAmount; float _HorizontalAmount; float _Speed; v2f vert (appdata 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 * _VerticalAmount; half2 uv = float2(i.uv.x / _HorizontalAmount, i.uv.y / _VerticalAmount); uv.x += column / _HorizontalAmount; uv.y -= row / _VerticalAmount; //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" }
下面就是针对不一样的行列。相同的速度数值也是大相径庭的速度。 图片
而后就是不一样的背景进远景对应的速度不一样。咱们看一下首先须要两张图分别是近景和远景_MainTex _DetailTex。而后就是分别他们的速度_ScrollX _Scroll2X。最后就是有关亮度_Multiplier。ip
T frac(T v)
//vert 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;
fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);
frac函数是返回变量的小数部分。其中的变量能够是float float2 float3 float4 都会分别对变量取小数。这样能够保证图片能够循环播放。由于上面每个顶点取偏移值值是同样的。而后须要混合颜色采用的lerp而后第三个变量取的是secondlayer的a通道是由于这种图是一张黑白图取值分别就是1或者0。即可以肯定近景中哪一些是镂空能够放远景的颜色。get
Shader "Unlit/11-2ScrollingBackground" { Properties { _MainTex ("BaseLayer(RGB)", 2D) = "white" {} _DetailTex ("2ndLayer(RGB)", 2D) = "white" {} _ScrollX ("BaseLayerScrollSpeed", Float) = 1.0 _Scroll2X ("2ndLayerScrollSpeed", Float) = 1.0 _Multiplier ("LayerMultiplier", Float) = 1 } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry" } Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 uv : TEXCOORD0; float4 pos : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _DetailTex; float4 _DetailTex_ST; float _ScrollX; float _Scroll2X; float _Multiplier; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); 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 firstLayer = tex2D(_MainTex, i.uv.xy); fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw); fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a); c.rgb *= _Multiplier; return c; } ENDCG } }FallBack "VertexLit" }
2、顶点动画
咱们先作一个2d的顶点动画。就是河流。咱们看一下咱们须要河流的纹理_MainTex,_Color调整总体颜色,控制水流动的幅度_Magniture,水流的波动_Frequency,波长的倒数_InvWaveLength(即该值越大,波长越小),流动速度_Speed.
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True" } //vert offset.yzw = float3(0,0,0); offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
DisableBatching关闭该tags是为了指明是否要对该SubShader进行批处理,由于这些须要特殊处理的Shader基本包括顶点动画。会合并与之相关的模型致使相关的顶点出现问题,合并模型会致使各自模型空间丢失(留个坑?)。顶点动画本质就是改变其中的顶点着色器中顶点的位置。这个的vertex是模型空间中的加上模型空间的位置份量。
Shader "Unlit/11-3Water" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _Magnitude ("Distortion Magnitude", Float) = 1 _Frequency ("Distortion Frequency", Float) = 1 _InvWaveLength ("InvWaveLength", Float) = 10 _Speed ("Speed", Float) = 0.5 } SubShader { 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 "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; float _Magnitude; float _Frequency; float _InvWaveLength; float _Speed; v2f vert (appdata v) { v2f o; float4 offset; offset.yzw = float3(0,0,0); 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 c = tex2D(_MainTex, i.uv); c.rgb *= _Color.rgb; return c; } ENDCG } }FallBack "VertexLit" }
另外一种顶点动画就是广告牌技术。会根据视角方向来旋转多边形,看上去好像面向摄像头,好比延误云朵闪光。其中的难处在于创建三个相互垂直的基向量。视角方向和向上的向量每每不垂直,因此要经过叉乘获得一个与二者相垂直的变量,而后再取该向量与视角方向叉积,更新向上的变量。
float3 center = float3(0,0,0); float3 viewer = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)); float3 normalDir = viewer - center; 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));
上面就是获得三个基向量的过程。_VerticalBillboarding主要是这个向量经过调整数值来改变normal变量来模拟特殊需求。好比草地下面的根部是不懂的。还有一些广告牌只会有旋转操做可是不会脱离垂直方向。
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject' Shader "Unlit/11-4Billboard" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _VerticalBillboarding ("VerticalBillboarding", Range(0, 1)) = 1 } SubShader { 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 "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; float _VerticalBillboarding; v2f vert (appdata 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; 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" }
而后能够看到咱们在作河流的时候河流的影子是不对的,咱们须要重写ShaderCaster Pass。
struct v2f { V2F_SHADOW_CASTER; ; v2f vert(appdata_base v){ v2f o; float4 offset; offset.yzw = float3(0,0,0); offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude; v.vertex += offset; TRANSFER_SHADOW_CASTER_NORMALOFFSET(o); return o; } fixed4 frag(v2f i) : SV_Target{ SHADOW_CASTER_FRAGMENT(i); }
这里采用了UnityCG.cginc中定义的一些宏。来计算阴影所需的内容。V2F_SHADOW_CASTER用于定义一些变量。
Shader "Unlit/11-5VertexAnimWithShadow" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _Magnitude ("Distortion Magnitude", Float) = 1 _Frequency ("Distortion Frequency", Float) = 1 _InvWaveLength ("InvWaveLength", Float) = 10 _Speed ("Speed", Float) = 0.5 } SubShader { Tags { "DisableBatching"="True" } Pass { Tags { "LightMode"="ForwardBase" } Cull off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; float _Magnitude; float _Frequency; float _InvWaveLength; float _Speed; v2f vert (appdata v) { v2f o; float4 offset; offset.yzw = float3(0,0,0); 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 c = tex2D(_MainTex, i.uv); c.rgb *= _Color.rgb; return c; } ENDCG } Pass{ Tags { "LightMode"="ShadowCaster" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_shadowcaster #include "UnityCG.cginc" float _Magnitude; float _Frequency; float _InvWaveLength; float _Speed; struct a2v { float4 vertex : POSITION; float4 texcoord : TEXCOORD0; }; struct v2f { V2F_SHADOW_CASTER; }; v2f vert(appdata_base v){ v2f o; float4 offset; offset.yzw = float3(0,0,0); offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude; v.vertex += offset; TRANSFER_SHADOW_CASTER_NORMALOFFSET(o); return o; } fixed4 frag(v2f i) : SV_Target{ SHADOW_CASTER_FRAGMENT(i); } ENDCG } }FallBack "VertexLit" }
最后就是一些小事项。取消批处理能够放置模型空间出现问题,可是会带来性能的问题,就是DrawCall增长了。因此应该避免一些在模型空间的计算,用顶点颜色存顶点到锚点的距离,避免使用模型空间中性做为锚点。
感谢你看到这里,Cheers!