1 树叶晃动原理
在模型空间下,对顶点进行X轴和Y轴上的偏移。
1.1 方法
通过计算顶点与Y轴单位向量的点积求得stage1。通过计算顶点与X轴单位向量的点积,加上时间分量_Time.y,求其正弦值stage2。将stage1/stage2与向量(0.001, 0, 0.001)相乘得到偏移值。
1.2 公式
V o f f s e t = ( V ⋅ x 1 ) ∗ s i n ( ( V ⋅ y 1 ) + _ T i m e . y ) ∗ z 1 V_{offset}=(V \cdot x_{1}) * sin((V \cdot y_{1}) + \_Time.y ) * z_{1} Voffset=(V⋅x1)∗sin((V⋅y1)+_Time.y)∗z1
其中 V V V是顶点坐标, x 1 x_{1} x1和 y 1 y_{1} y1分别位X轴和Y轴单位向量, z 1 = ( 0.001 , 0 , 0.001 ) z_{1}=(0.001,0,0.001) z1=(0.001,0,0.001)。
2 单个面片晃动实现
3 整个树木晃动实现
4 Shader代码
- 树叶纹理贴图资源下载
- 树木模型资源下载
Shader "Unlit/MyTreeShader"
{Properties{_MainTex ("纹理图片", 2D) = "white" {}_Strength("摇摆幅度", Float) = 1_Speed("摇摆速度", Float) = 3_AoColor("基础色", Color) = (1,1,1)_ShadowColor("阴影色", Color) = (1,1,1)_Specular("高光色", Color) = (1,1,1)_Gloss("光泽度", Float) = 1}SubShader{ Pass{//指明该Pass的光照模式Tags {"LightMode" = "ForwardBase"}Cull OffCGPROGRAM#pragma vertex vert#pragma fragment frag//包含进Unity的内置变量#include "UnityCG.cginc" #include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float4 color : COLOR0;float3 normal: NORMAL;};struct v2f{float2 uv : TEXCOORD0;float4 pos : SV_POSITION;float4 color: TEXCOORD1;float3 worldNormal: TEXCOORD2;float3 worldPos : TEXCOORD3;};sampler2D _MainTex;float4 _MainTex_ST;fixed3 _AoColor;float _Speed;float _Strength;fixed3 _ShadowColor;fixed3 _Specular;float _Gloss;v2f vert (appdata v){v2f o;float3 worldPos = UnityObjectToWorldDir(v.vertex);float stage1 = dot(v.vertex, float3(0, 1, 0)) * _Strength;float stage2 = sin(dot(v.vertex, float3(1, 0, 0)) * _Strength + _Time.y * _Speed);float3 stage3 = stage1 * stage2 * float3(0.001, 0, 0.001) * v.color.a; o.pos = UnityObjectToClipPos(v.vertex + stage3);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.color = v.color;o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);clip (col.a - 0.5);// 世界空间下的法线fixed3 worldNormal = i.worldNormal;// 世界空间下光源方向fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);// 漫反射fixed3 diffuse = _LightColor0.rgb * col.rgb * _AoColor* lerp(_ShadowColor, float3(1,1,1), i.color.rgb)* (dot(worldNormal, worldLightDir) * 0.5 + 0.5);// 环境光fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * col.rgb;// 世界空间下的视角方向fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);// 世界空间下的half方向fixed3 halfDir = normalize(worldLightDir + viewDir);// 高光fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);return fixed4(diffuse + ambient + specular, col.a);}ENDCG}}
}
5 关于树木颜色
5.1 漫反射
树木的漫反射使用半兰伯特光照模型,同时使用环境光、贴图颜色和阴影颜色插值。其中阴影颜色插值是使用自定义阴影色、纯白色和模型顶点颜色三者做lerp运算得到的。公式如下
C s h a d o w = L e r p ( _ S h a d o w C o l o r , f l o a t 3 ( 1 , 1 , 1 ) , V c o l o r ) C_{shadow} = Lerp(\_ShadowColor, float3(1,1,1), V_{color}) Cshadow=Lerp(_ShadowColor,float3(1,1,1),Vcolor)
其中 _ S h a d o w C o l o r \_ShadowColor _ShadowColor为自定义阴影颜色, f l o a t 3 ( 1 , 1 , 1 ) float3(1,1,1) float3(1,1,1)则是纯白色, V c o l o r V_{color} Vcolor则是模型顶点颜色。
其中 L e r p Lerp Lerp函数的计算公式为:
L e r p ( a , b , w ) = a + ( b − a ) ∗ w Lerp(a, b, w) = a + (b - a) * w Lerp(a,b,w)=a+(b−a)∗w
当 w = 0 w=0 w=0时, L e r p ( a , b , w ) = a Lerp(a, b, w) = a Lerp(a,b,w)=a;当 w = 1 w=1 w=1时, L e r p ( a , b , w ) = b Lerp(a, b, w) = b Lerp(a,b,w)=b。
所以 L e r p Lerp Lerp函数的意义是根据参数 w w w在 a a a~ b b b之间取一个合适的插值。当 w w w为线性变化时,则 L e r p Lerp Lerp函数取值就会呈线性变化。
通过调节基础色和阴影色,进行漫反射颜色的设置。
5.2 高光反射
使用Blinn-Phong光照模型,可通过调节高光色和光泽度两个参数进行高光的设置。