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 Off
CGPROGRAM
#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 texture
fixed4 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光照模型,可通过调节高光色和光泽度两个参数进行高光的设置。