【Unity Shader】树叶晃动效果原理及实现

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=(Vx1)sin((Vy1)+_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+(ba)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光照模型,可通过调节高光色和光泽度两个参数进行高光的设置。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路易斯·李

点个赞再走呗 :)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值