UnityShader基础(六)——动画效果

一、纹理动画

        Unity提供了一些内置变量用于做动画效果,比如_Time变量等,其中_Time.y代表从游戏开始到现在的正常时间。

        1.1 序列帧动画

                对一张纹理进行偏移采样,首先计算出纹理动画是怎么分的,即这个时间段的对应行列,然后对原始的采样坐标先做一个比例缩放,然后加上偏移得到行列对应的采样坐标。

                原版写法:

Shader "Custom/Test0"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _AnimationTex("动画纹理",2D)="white"{}
        _AnimationSpeed("动画速度",float)=1.0
        _HorAmount("水平数量",float)=8
        _VerAmount("垂直数量",float)=8
        
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            fixed4 _Color;
            sampler2D _AnimationTex;
            float4 _AnimationTex_ST;
            fixed _AnimationSpeed;           
            fixed _HorAmount;
            fixed _VerAmount;            

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float2 uv:TEXCOORD3;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                o.uv=TRANSFORM_TEX(v.texcoord,_AnimationTex);
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               float time=_Time.y*_AnimationSpeed;
               float row=(floor(time/_HorAmount)+1)%_HorAmount;
               float col=floor(time%_HorAmount)+1;
               //x方向偏移
               i.uv.x/=_HorAmount;
               i.uv.x+=(col-1)/_HorAmount;
               //y方向偏移
               i.uv.y/=_VerAmount;
               i.uv.y+=(_VerAmount-row)/_VerAmount;
               //偏移后的结果采样 
               fixed3 texResult=tex2D(_AnimationTex,i.uv); 
                                               
               return fixed4(texResult,1);                 
                
            }
            
            ENDCG
        }
    }
}

                结果:动态效果就不展示了,和用animation一下一下k帧等的效果一样,只是这种更方便高效。

                不足:如果在inspector界面调节纹理的offset值,会出现纹理闪烁的效果,参考书中的写法也没解决闪烁问题。

                出现原因:当物体的采样坐标偏移时,在纹理本身行列不变情况下,缩放后再加上行列的偏移,会使此时的uv坐标中的u坐标会超出1,即采样到了图片外,但使用了repeat模式,会将大于一的数值强制截取小数,一截取,u坐标直接回到了这一行的第一个图片,找到原因后解决就很简单了,要做的就是大于1的时候回到最后一张图片,v坐标同理。

                改良版:        

Shader "Custom/Test0"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _AnimationTex("动画纹理",2D)="white"{}
        _AnimationSpeed("动画速度",float)=1.0
        _HorAmount("水平数量",float)=8
        _VerAmount("垂直数量",float)=8
        
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            fixed4 _Color;
            sampler2D _AnimationTex;
            float4 _AnimationTex_ST;
            fixed _AnimationSpeed;           
            fixed _HorAmount;
            fixed _VerAmount;            

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float2 uv:TEXCOORD3;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                o.uv=TRANSFORM_TEX(v.texcoord,_AnimationTex);
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               float time=_Time.y*_AnimationSpeed;
               float row=(floor(time/_HorAmount)+1)%_HorAmount;
               float col=floor(time%_HorAmount)+1;
               //x方向偏移
               i.uv.x=(i.uv.x%1)/_HorAmount;
               i.uv.x+=(col-1)/_HorAmount;
               //y方向偏移
               i.uv.y=(i.uv.y%1)/_VerAmount;
               i.uv.y+=(_VerAmount-row)/_VerAmount;
               //偏移后的结果采样 
               fixed3 texResult=tex2D(_AnimationTex,i.uv); 
                                               
               return fixed4(texResult,1);                 
                
            }
            
            ENDCG
        }
    }
}

                在计算采样时考虑偏移就行,对一取余。对于偏移正常处理即可。                                  

          1.2 背景滚动动画

                纹理采样加上x轴的变化即可。

Shader "Custom/Test0"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _FarBackGround("较远背景纹理",2D)="white"{}
        _NearBackGround("较近背景纹理",2D)="white"{}
        _FarSpeed("较远背景纹理",float)=1.0
        _NearSpeed("较近背景纹理",float)=1.0
        
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _FarBackGround;            
            sampler2D _NearBackGround;
            float4 _FarBackGround_ST;
            float4 _NearBackGround_ST;
            fixed _FarSpeed;
            fixed _NearSpeed;

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float4 uv:TEXCOORD0;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_FarBackGround)+float2(_Time.y*_FarSpeed,0);
                o.uv.zw=TRANSFORM_TEX(v.texcoord,_NearBackGround)+float2(_Time.y*_NearSpeed,0);
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               fixed4 FarResult=tex2D(_FarBackGround,i.uv.xy);
               fixed4 NearResult=tex2D(_NearBackGround,i.uv.zw);                
               fixed3 color=lerp(FarResult,NearResult,NearResult.a);                                
               return fixed4(color,1);                 
                
            }
            
            ENDCG
        }
    }
}

二、顶点动画 

        做顶点动画时一定要注意加上DisableBatching标签,防止优化时对模型进行批处理,批处理会丢失模型本身的顶点信息,当然我们也可以将顶点相对于原点的偏离存储在顶点颜色中,就不用担心批处理的问题。

        2.1 二维河流动画:

                原理也是很简单,用正弦函数模拟坐标偏移即可。

                注意水流效果都由哪些偏移组成,首先是uv偏移,模拟水流移动,其次是顶点偏移,模拟水流起伏。

                这里也有一个小坑,那就是不要用Unity自带的Quad!!!,我一开始得到的效果巨怪,还以为自己写错了,谁知道是因为Quad只有4个顶点,根本做不出来sin的效果,说多了都是泪,参考书的Github网址有专门的多顶点Quad。        

                第一版写法:        

Shader "Custom/Test0"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("水流贴图",2D)="white"{}
        _HorizontalSpeed("水流速度",float)=1.0
        _VerticalSpeed("起伏速度",float)=1.0
        _Amount("起伏数量",float)=1.0
        _Offset("起伏偏移",float)=1.0
        //确保多个材质的初始的画面不同
        _InitialTime("初始时间",float)=1.0
        
    }
    SubShader
    {
        Tags{"DisableBatching"="True"}
        Pass
        {
            //开启透明度混合的原因是书中提供的素材图片周围有一圈白环
            //而白环的透明度是0,开启透明度混合可以过滤透明度为0的地方
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            fixed4 _Color;
            sampler2D _MainTex;                        
            float4 _MainTex_ST;            
            fixed _HorizontalSpeed;
            fixed _VerticalSpeed;
            fixed _Offset;
            fixed _Amount;
            fixed _InitialTime;
            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float2 uv:TEXCOORD0;
 
            };
            
            v2f vert(a2v v)
            {
                
                v2f o;                
                float3 offset=float3(0,0,0);
                //模型使用的方向是z轴滚动,所以用z轴充当sin函数的变量值
                offset.x=sin((v.vertex.z+_VerticalSpeed*(_Time.y+_InitialTime))*_Amount)*_Offset/100;                
                o.pos=UnityObjectToClipPos(v.vertex+offset);                
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
                o.uv+=float2(0,_Time.y*_HorizontalSpeed);
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               fixed3 color=tex2D(_MainTex,i.uv.xy)*_Color;                                                                              
               return fixed4(color,0.5);                                 
            }
            
            ENDCG
        }
    }
}

       这种写法虽然实现了顶点偏移,但实际效果却很一般。

                 可以看到,上面的偏移和下面的偏移过于相似,导致实际效果看起来有些刻意。下面将展示效果更好的一种写法,有时候不得不佩服某些人的脑洞。 

                第二版写法:

Shader "Custom/Test0"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("水流贴图",2D)="white"{}
        _HorizontalSpeed("水流速度",float)=1.0
        _VerticalSpeed("起伏速度",float)=1.0
        _Amount("起伏数量",float)=1.0
        _Offset("起伏偏移",float)=1.0
        //确保多个材质的初始的画面不同
        _InitialTime("初始时间",float)=1.0
        
    }
    SubShader
    {
        Tags{"DisableBatching"="True"}
        Pass
        {
            //开启透明度混合的原因是书中提供的素材图片周围有一圈白环
            //而白环的透明度是0,开启透明度混合可以过滤透明度为0的地方
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            fixed4 _Color;
            sampler2D _MainTex;                        
            float4 _MainTex_ST;            
            fixed _HorizontalSpeed;
            fixed _VerticalSpeed;
            fixed _Offset;
            fixed _Amount;
            fixed _InitialTime;
            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float2 uv:TEXCOORD0;
 
            };
            
            v2f vert(a2v v)
            {
                
                v2f o;                
                float3 offset=float3(0,0,0);
                //模型使用的方向是z轴滚动,所以用z轴充当sin函数的变量值
                offset.x=sin((v.vertex.z+v.vertex.x+_VerticalSpeed*(_Time.y+_InitialTime))*_Amount)*_Offset/100;                
                o.pos=UnityObjectToClipPos(v.vertex+offset);                
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
                o.uv+=float2(0,_Time.y*_HorizontalSpeed);
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               fixed4 color=tex2D(_MainTex,i.uv.xy)*_Color;                                                                              
               return fixed4(color);                                 
            }
            
            ENDCG
        }
    }
}

                sin变量的取值加上顶点的x值,实现某些地方看起来更大,某些地方看起来更窄。

                 这个效果就看起来自然了很多。                            

        2.2 广告牌技术:

                无论观察者处于什么位置观察任何图像,广告版都能提供一个朝向观众的面,这个面随着摄像机的改变而改变。

                BillBoard技术是计算机图形学领域中进行快速绘制的一种方法。在类似游戏这种对实时性要求较高的场景下采取BillBoard技术可以大大加快绘制的速度从而提高画面的流畅性。把3D的物体用2D来表示,然后让该物体始终朝向镜头。比如场景中的一棵树,对于整个场景来说不是主要物体,因此无需花费大量的时间去计算树的每一部分的细节。通常的做法是首先准备好一张树的照片,然后镜头运动的时候使得树始终正对着镜头,我们看到的始终是树的正面。我们在渲染树木,灌木丛,云,烟等效果时都会用到。

                广告牌技术的难点在于构建旋转矩阵,保证广告牌面的正方向永远面向玩家。

                广告牌的写法有两种,一个是面法线永远指向视角方向,另一个是向上的方向永远是(0,1,0),而法线永远是尽力指向视角方向,我们在shader中用一个数值来控制法线指向视角的程度。

                旋转矩阵的构建我们知道用坐标轴填充,同时基于模型空间的原点。当然,你可以不基于模型的原点,只需将旋转矩阵扩充到齐次坐标空间,最后一列填充偏移值。关于旋转矩阵的填充方式,这里有一个巨坑,我们的广告牌一般都是一张平面图,意味着有正面和反面,为了保证旋转后的面我们仍然可以看到,旋转矩阵内列向量的摆放至关重要。

                如果你看过参考书,你会发现书上的写法只针对quad,而plane则会看不到任何结果,可能会有些像素点,大概率由于浮点数精度造成,plane看不到结果是因为,plane只有在y方向才是面的正方向,而quad是在z轴,书中这个旋转矩阵的构建,会使plane模型下的z轴朝向我们,在z轴的方向我们肯定看不到任何结果。

                2.2.1 针对quad的竖直排列方式:        

Shader "Custom/Test1"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("纹理贴图",2D)="white"{}
        _Level("法线朝向控制",Range(0,1))=1
    }
    SubShader
    {
        Tags{"DisableBatching"="True"}
        Pass
        {
            Cull off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;                        
            float4 _MainTex_ST;            
            fixed _Level;

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float4 uv:TEXCOORD0;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;                
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
                //假设向上的方向永远是(0,1,0),先根据法线计算出另一个坐标轴
                //先计算出模型空间下的视角方向,注意我们要的是面朝向我们,
                float3 viewDir=mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
                float3 normalDir=viewDir;
                normalDir.y=normalDir.y*_Level;
                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));
                //旋转矩阵构建时,把视角放最后一行,确保旋转后面的z轴朝向我们
                float3 localPos=float3(0,0,0)+rightDir*v.vertex.x+upDir*v.vertex.y+normalDir*v.vertex.z;
                o.pos=UnityObjectToClipPos(float4(localPos,1));
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               fixed3 color=tex2D(_MainTex,i.uv.xy);                                                                              
               return fixed4(color,1);                 
                
            }
            
            ENDCG
        }
    }
}

                2.2.2 plane的水平排列方式:        

Shader "Custom/Test0"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("纹理贴图",2D)="white"{}
        _Level("法线朝向控制",Range(0,1))=1
    }
    SubShader
    {
        Tags{"DisableBatching"="True"}
        Pass
        {
            Cull off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;                        
            float4 _MainTex_ST;            
            fixed _Level;

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float4 uv:TEXCOORD0;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;                
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
                //假设向上的方向永远是(0,1,0),先根据法线计算出另一个坐标轴
                //先计算出模型空间下的视角方向,注意我们要的是面朝向我们,
                float3 normalDir=mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
                //通过控制y值,实现up方向的控制
                normalDir.y=normalDir.y*_Level;
                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));
                //顶点*旋转矩阵,这里拆开写了,把视角放中间一行,确保旋转后面的y轴朝向我们
                float3 localPos=float3(0,0,0)+rightDir*v.vertex.x+normalDir*v.vertex.y+upDir*v.vertex.z;
                //旋转只针对顶点,并不针对模型坐标空间,所以还要作矩阵变换
                o.pos=UnityObjectToClipPos(float4(localPos,1));
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {
                
               fixed3 color=tex2D(_MainTex,i.uv.xy);                                                                              
               return fixed4(color,1);                 
                
            }
            
            ENDCG
        }
    }
}

                关于针对偏移的问题,只需要在开始normal计算上加上中心点偏移,同时,旋转矩阵的计算也加上偏移即可。

                2.2.3 针对quad的偏移写法:                

Shader "Custom/Test1"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("纹理贴图",2D)="white"{}
        _Level("法线朝向控制",Range(0,1))=1
        Center("中心点",Vector)=(0,0,0)
    }
    SubShader
    {
        Tags{"DisableBatching"="True"}
        Pass
        {
            Cull off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;                        
            float4 _MainTex_ST;            
            fixed _Level;
            fixed3 Center;

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float4 uv:TEXCOORD0;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;                
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
                //假设向上的方向永远是(0,1,0),先根据法线计算出另一个坐标轴
                //先计算出模型空间下的视角方向,注意我们要的是面朝向我们
                //计算出在center下的视角方向
                float3 viewDir_Old=mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
                float3 tempDir=float3(0,0,0)-Center;
                float3 viewDir=tempDir+viewDir_Old;                
                float3 normalDir=viewDir;
                normalDir.y=normalDir.y*_Level;
                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));
                //旋转矩阵构建时,把视角放最后一行,确保旋转后面的z轴朝向我们
                float3 localPos=Center+rightDir*v.vertex.x+upDir*v.vertex.y+normalDir*v.vertex.z;
                o.pos=UnityObjectToClipPos(float4(localPos,1));
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               fixed3 color=tex2D(_MainTex,i.uv.xy);                                                                              
               return fixed4(color,1);                 
                
            }
            
            ENDCG
        }
    }
}

                问题:上面三种写法无法屏蔽模型本身旋转的影响,这里的旋转只是对顶点的变换,对于整个模型空间(即决定模型空间三个坐标轴)并未有任何变化。同时,问题二如果视角方向和向上,向下的差距不大时,图片会有一个明显的翻转。

                问题一的优化:说白了就是模型旋转导致顶点的旋转矩阵计算时,那个向上的向量也同步变换,我们在模型变换时,对向上的向量也做一次变换,使得向上的向量仍保持向上。

                2.2.4 针对问题一的quad优化:                

Shader "Custom/Test1"
{
    Properties
    {
        //用于控制纹理的整体颜色表现
        _Color("Color",Color)=(1,1,1,1)
        _MainTex("纹理贴图",2D)="white"{}
        _Level("法线朝向控制",Range(0,1))=1
        Center("中心点",Vector)=(0,0,0)
    }
    SubShader
    {
        Tags{"DisableBatching"="True"}
        Pass
        {
            Cull off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;                        
            float4 _MainTex_ST;            
            fixed _Level;
            fixed3 Center;

            struct a2v
            {
                float4 vertex:POSITION;
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;           
                float4 uv:TEXCOORD0;                       
            };
            
            v2f vert(a2v v)
            {
                v2f o;                
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
                //假设向上的方向永远是(0,1,0),先根据法线计算出另一个坐标轴
                //先计算出模型空间下的视角方向,注意我们要的是面朝向我们
                //计算出在center下的视角方向
                float3 viewDir_Old=mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
                float3 tempDir=float3(0,0,0)-Center;
                float3 viewDir=tempDir+viewDir_Old;                
                float3 normalDir=viewDir;
                normalDir.y=normalDir.y*_Level;
                normalDir=normalize(normalDir);
                float3 upDir=abs(normalDir.y)>0.999?mul(unity_WorldToObject,float3(0,0,1)):mul(unity_WorldToObject,float3(0,1,0));
                float3 rightDir=normalize(cross(upDir,normalDir));
                upDir=normalize(cross(normalDir,rightDir));
                //旋转矩阵构建时,把视角放最后一行,确保旋转后面的z轴朝向我们
                float3 localPos=Center+rightDir*v.vertex.x+upDir*v.vertex.y+normalDir*v.vertex.z;
                o.pos=UnityObjectToClipPos(float4(localPos,1));
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {                
               fixed3 color=tex2D(_MainTex,i.uv.xy);                                                                              
               return fixed4(color,1);                 
                
            }
            
            ENDCG
        }
    }
}

                这样,无论模型本身怎么旋转都可以屏蔽掉这个影响。

                关于问题二的优化:能力有限,暂时无法解决。

                补充:你其实可以将我们上面提到的河流和广告牌结合起来,代码我就不展示了,可以看看自己对广告牌的理解是否透彻,毕竟顶点不仅要旋转,还要位移。

三、关于顶点动画的阴影问题

                顶点动画的阴影效果,我们必须要在Pass中实现自定义的效果,而不是仅仅使用Unity内置的阴影Shader,大致就是做阴影是考虑顶点变换,而不是在顶点变换之前就生成阴影纹理。

猜你喜欢

转载自blog.csdn.net/SliverAsh_/article/details/127043233