Unity Shader学习笔记(18)纹理动画、顶点动画、广告牌技术
发表于2018-01-02
程序纹理的内容介绍完,乘热打铁,现在就给大家介绍下纹理动画、顶点动画和广告牌技术,这三类在开发过程中也是用的比较多的,分享给大家,希望能对大家有帮助。
Unity Shader的内置变量
纹理动画
可用于代替粒子系统模拟动画效果。
帧序列动画
8x8帧序列纹理动画
Properties { _Color ("Color Tint", Color) = (1, 1, 1, 1) _MainTex ("Image Sequence", 2D) = "white" {} // 包含所有关键帧图像的纹理 _HorizontalAmount ("Horizontal Amount", Float) = 4 // 水平方向包含关键帧图像个数 _VerticalAmount ("Vertical Amount", Float) = 4 // 同上(竖直方向) _Speed ("Speed", Range(1, 100)) = 30 // 播放速度 }
因为序列帧图像通常是透明纹理,所以就按半透明的方式设置。
// 序列帧图像通常包含透明通道 Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} Pass { Tags { "LightMode"="ForwardBase" } // 关闭深度写入,开启混合模式 ZWrite Off Blend SrcAlpha OneMinusSrcAlpha ... fixed4 frag (v2f i) : SV_Target { float time = floor(_Time.y * _Speed); // floor()取整。CG的函数 float row = floor(time / _HorizontalAmount); // 行索引 float column = time % _HorizontalAmount; // 列索引 // 纹理坐标从上到下,所以是-row。下面两种计算方法 //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; } }
滚动动画
可以模拟2D跑酷游戏背景的视察效果。
Properties { _MainTex ("Base Layer (RGB)", 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 // 纹理亮度 }
v2f vert (a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); // TRANSFORM_TEX 得到初始纹理坐标,加上偏移坐标。frac为获取小数部分的值。 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; }
顶点动画
即修改顶点信息的动画。
一张长方形纹理,通过修改顶点信息,实现扭曲效果。
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 }
// DisableBatching:取消批处理。需要对模型空间下的顶点位置进行偏移,所以就不合并模型。 Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"} Pass { Tags { "LightMode"="ForwardBase" } ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Cull Off // 关闭剔除模式(正反面都显示) ... v2f vert(a2v v) { v2f o; float4 offset; offset.yzw = float3(0.0, 0.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.0, _Time.y * _Speed); return o; } }
注意到前面使用内置阴影的Pass是没有处理顶点动画的,所以最后投影的阴影是错误的,需要自定义ShadowCasterPass,在这个Pass做相同顶点变换即可。
下面用到阴影的宏都在UnityCG.cginc中定义。
Pass { Tags { "LightMode" = "ShadowCaster" } ... #pragma multi_compile_shadowcaster ... 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 }
广告牌技术(Billboarding)
即让纹理始终面对着镜头。本质就是构建旋转矩阵,三个基向量:表面法线、向上的方向、向右的方向。还需要指定一个锚点,即旋转中心。
计算方法(如下图):
1. 先计算向右方向 right = up × normal。起始up是竖直向上的。
2. 叉乘即可得到向上方向 up’ = normal × right。
Vertical Restraints值为1,所有星星不管原来什么角度都会转向到镜头。
Vertical Restraints值为0,所有星星固定向上方向,并最大限度面向镜头。
Properties { _MainTex ("Main Tex", 2D) = "white" {} _Color ("Color Tint", Color) = (1, 1, 1, 1) _VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1 // 垂直程度,0:固定向上,1:固定法线 }
// 需要模型空间下的位置来作为锚点计算,所以要关掉批处理。 Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"} Pass { Tags { "LightMode"="ForwardBase" } ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Cull Off CGPROGRAM ... 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 为1为法线固定(始终向上),为0为向上方向固定。 normalDir.y =normalDir.y * _VerticalBillboarding; normalDir = normalize(normalDir); // 通过normalDir.y 先判断法线和向上是否平行(叉积会错),来改变向上方向 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; }
关于 DisableBatching
如果要进行一些顶点动画,就要关闭批处理。性能会下降,所以尽量避免使用模型空间下的绝对位置和方向进行计算。如广告拍技术中,可以用顶点颜色来存储顶点到锚点的距离。
Unity Shader学习笔记系列教程: