先看效果:
飘动全是使用shader偏移顶点
的位置做的
几个点:
1、顶点数据的alpha值,标识了受风的权重。alpha越大,受风越厉害。
通过观察模型可以看出这点。而shader里这句也有说明。
以_Time作为参数,使用frac来取模,形成周期性的动画。
下面是注释后的shader
// - Unlit
// - Per-vertex (virtual) camera space specular light
// - SUPPORTS lightmap
Shader "MADFINGER/Environment/Lightmap + Wind" {
Properties {
_MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
_Wind("Wind params",Vector) = (1,1,1,1)
_WindEdgeFlutter("Wind edge fultter factor", float) = 0.5
_WindEdgeFlutterFreqScale("Wind edge fultter freq scale",float) = 0.5
}
SubShader {
Tags {"Queue"="Transparent" "RenderType"="Transparent" "LightMode"="ForwardBase"}
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
Cull Off ZWrite Off
CGINCLUDE
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
samplerCUBE _ReflTex;
#ifndef LIGHTMAP_OFF //lightmap是否开启。如果没有关闭lightmap。就给lightmap支持下。
float4 unity_LightmapST;
sampler2D unity_Lightmap;
#endif
float _WindEdgeFlutter; //风导致旗帜震动的振幅
float _WindEdgeFlutterFreqScale; //风的时间流逝速度,影响频率,控制旗子反复摆动的周期
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
#ifndef LIGHTMAP_OFF //开启lightmap的时候,要使用
float2 lmap : TEXCOORD1;
#endif
fixed3 spec : TEXCOORD2;
};
inline float4 AnimateVertex2(float4 pos, float3 normal, float4 animParams,float4 wind,float2 time)
{
// animParams stored in color
// animParams.x = branch phase
// animParams.y = edge flutter factor
// animParams.z = primary factor
// animParams.w = secondary factor
float fDetailAmp = 0.1f;
float fBranchAmp = 0.3f;
// Phases (object, vertex, branch)
float fObjPhase = dot(_Object2World[3].xyz, 1); //这一行直接写1也行,_Object2World[3],一般情况下应该是0,0,0,1
float fBranchPhase = fObjPhase + animParams.x; //由于animParams.x是0,这个也是1
float fVtxPhase = dot(pos.xyz, animParams.y + fBranchPhase); //根据x,y,z,和振幅+1点乘。离远点越远,这个值越大。受振幅影响
// x is used for edges; y is used for branches
float2 vWavesIn = time + float2(fVtxPhase, fBranchPhase ); //vWavesIn第一项是点的相位*时间流逝率,第二项就是时间+1.
// 1.975, 0.793, 0.375, 0.193 are good frequencies
float4 vWaves = (frac( vWavesIn.xxyy * float4(1.975, 0.793, 0.375, 0.193) ) * 2.0 - 1.0); // 将结果乘以频率,对一取模,然后乘以2,再减一
vWaves = SmoothTriangleWave( vWaves ); //一个优化的正弦波计算。
float2 vWavesSum = vWaves.xz + vWaves.yw; //振幅叠加,以免太正弦了。
// Edge (xz) and branch bending (y)
float3 bend = animParams.y * fDetailAmp * normal.xyz; //沿法线方向根据振幅和参数缩放一个值
bend.y = animParams.w * fBranchAmp; //y根据受风能力,乘以一个参数
pos.xyz += ((vWavesSum.xyx * bend) + (wind.xyz * vWavesSum.y * animParams.w)) * wind.w; //再次根据受风能力进行影响。
// Primary bending
// Displace position
pos.xyz += animParams.z * wind.xyz; //根据受风能力,再次扩大。
return pos;
}
v2f vert (appdata_full v)
{
v2f o;
float4 wind;
float bendingFact = v.color.a;
wind.xyz = mul((float3x3)_World2Object,_Wind.xyz); //风的方向转为物体坐标。wind参数的xyz表示风的世界坐标的方向。_Wind为风的个方向强度
wind.w = _Wind.w * bendingFact; //每个顶点的颜色的alpha值表示此点受风变化的强度。旗子底部这个值较大。wind参数的w表示风力
float4 windParams = float4(0,_WindEdgeFlutter,bendingFact.xx); //0,旗子振幅,每个顶点的受风强度值
float windTime = _Time.y * float2(_WindEdgeFlutterFreqScale,1); //风的时间流逝速度
float4 mdlPos = AnimateVertex2(v.vertex,v.normal,windParams,wind,windTime); //计算受风之后的位置
o.pos = mul(UNITY_MATRIX_MVP,mdlPos);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.spec = v.color;
#ifndef LIGHTMAP_OFF
o.lmap = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
#endif
return o;
}
ENDCG
Pass {
CGPROGRAM
#pragma debug
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest //片元着色器选择低精度的计算方式。5.0以后抛弃掉 fragmentoption的声明。
fixed4 frag (v2f i) : COLOR
{
fixed4 tex = tex2D (_MainTex, i.uv);
fixed4 c;
c.rgb = tex.rgb;
c.a = tex.a;
#ifndef LIGHTMAP_OFF
fixed3 lm = DecodeLightmap (tex2D(unity_Lightmap, i.lmap));
c.rgb *= lm;
#endif
return c;
}
ENDCG
}
}
}
中间多次根据风力进行修正的地方,作者写的蛮复杂,不写那么复杂,效果看起来也还行。