美文网首页
【Unity Shader入门精要学习】让画面动起来(二)

【Unity Shader入门精要学习】让画面动起来(二)

作者: 小王子称号发放NPC | 来源:发表于2019-10-29 10:15 被阅读0次

顶点动画

流动的河流

它的原理通常是使用正弦函数等来模拟水流的波动效果。主要是改变了模型空间点的位置。


Shader "Unlit/VertexAnimatorWater"
{
    Properties{
        _MainTex("Main Texture",2D)="white"{}
        _Color("Color",Color)=(1,1,1,1)
        _Magnitude("Distortion Magnitude",float)=1  //振幅
        _Frequency("Distortion Frequency",float)=1 //频率
        _InvWaveLength("Distortion Inverse Wave Length",float)=1 //用于控制波长
        _Speed("Speed",float)=1
    }

    SubShader {
        //需要关闭批处理,因为顶点动画改变了模型空间的点的坐标
        Tags { "DisableBatching"="True"}
        Pass{
            Tags{"LightMode"="ForwardBase"}
            Cull off
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            float _Magnitude;
            float _Frequency;
            float _InvWaveLength;
            float _Speed;

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
            };

            v2f vert(a2v v)
            {
                v2f o;
                
                float4 offset = float4(0,0,0,0);
                //这个就是正弦波长公式:y=Asin(ωx+φ)
                //然后进行顶点动画
                offset.x = _Magnitude * sin(_Frequency * _Time.y + _InvWaveLength*(v.vertex.x + v.vertex.y + v.vertex.z));
                o.pos = UnityObjectToClipPos(v.vertex + offset);

                o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                //进行纹理动画
                o.uv += float2(0,_Speed * _Time.y);

                return o;
            }

            fixed4 frag(v2f i):SV_TARGET
            {
                fixed4 col = tex2D(_MainTex,i.uv);
                col.rgb *= _Color.rgb;

                return col;
            }

            ENDCG
        }
      //注意阴影的处理,写入深度纹理的点的位置需要同时改变
        Pass{
            Tags { "LightMode" = "ShadowCaster" }

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"


            float _Magnitude;
            float _Frequency;
            float _InvWaveLength;
            float _Speed;

            struct v2f
            {
                V2F_SHADOW_CASTER;
            };

            v2f vert(appdata_base v)
            {
                v2f o;
                
                float4 offset = float4(0,0,0,0);
                offset.x = _Magnitude * sin(_Frequency * _Time.y + _InvWaveLength*(v.vertex.x + v.vertex.y + v.vertex.z));
                v.vertex = v.vertex + offset;

                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)

                return o;
            }

            fixed4 frag(v2f i):SV_TARGET
            {
                SHADOW_CASTER_FRAGMENT(i);
            }

            ENDCG
        }
    }

   // Fallback "VertexLit"
}

广告牌

广告牌技术(Billboarding),是常见的顶点动画,他根据视角方向来旋转一个被纹理作色的多边形,使得多边形看起来好像总是面对着摄像机。
广告牌技术的本质是构建旋转矩阵,而一个变换需要3个基向量(XYZ)。广告牌技术使用的基向量通常是表面法线(normal)、指向上的方向(up)、指向右的方向(right),除此之外还需要指定一个锚点(anchor location),这个锚点在旋转过程中位置是不变的,以此来确定多边形在空间的位置。
广告牌技术的难点在于构建3个相互正交的基向量,首先选择要固定的方向,要不然就是固定法线方向,也就是视角方向,要不然就固定向上的方向,假设我们固定了视角方向,然后就可以计算出向右的方向,这个方向一定是垂直于法线方向和向上方向组成的平面的,所以通过叉乘可以计算出向右的方向:

向右的方向
计算出向右的方向之后就可以计算出真正的向上的方向,这个向上的方向一定垂直于视角和向右的方向组成的平面(因为是3个正交基一定是相互垂直的):
image.png
三个正交基
Shader "Unlit/Billboard"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _VerticalBillboard("Vertical Restraints",Range(0,1))=1
    }
    SubShader
    {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}

        Pass
        {
            Tags{"LightMode"="ForwardBase"}

            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct a2v
            {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _VerticalBillboard;

            v2f vert (a2v v)
            {
                v2f o;

                float3 center = float3(0,0,0);
                float3 vivwer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
                float3 normalDir = vivwer - center;
                normalDir.y = normalDir.y * _VerticalBillboard;
                normalDir = normalize(normalDir);

                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 offsets = v.vertex.xyz - center;

                float3 localPos = center + offsets.x * rightDir + offsets.y * upDir + offsets.z * normalDir;
                float4 transPos = float4(localPos,1);

                o.pos = UnityObjectToClipPos(transPos);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
           
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

相关文章

网友评论

      本文标题:【Unity Shader入门精要学习】让画面动起来(二)

      本文链接:https://www.haomeiwen.com/subject/wydivctx.html