Unity Shader 序列帧动画原理

序列帧动画


帧序列动画其实就是快速变换采样的uv值,在短时间内以一定的顺序采集完一张动图以实现动画的效果

1.uv坐标的说明以及offset

uv坐标,也就是纹理对应的坐标,它一般为0到1,且是从坐下到右上角,分别记录了整张纹理的信息,我们知道在Shader成像时,它最终会在0到1的屏幕上进行采样,这也对应了uv坐标的0到1.所以我们如果对uv坐标进行一定规则的更改也就会改变最终的成像

为了直观,我们这里用一张九宫格纹理,里面是1-9的数字.首先将这张纹理添加到材质球中,我们会看到图片变成了纹理呈现到了屏幕上。
此时我们将采样的uv坐标整体除以3,也就是采样时只会采样纹理中(0,0.333)部分呈现给我们,这里呈现出了7,符合我们的猜想,那我们想让其呈现出1呢?我们第一时间想到的就是采样uv.x = (0,0.333) uv.y = (0.66666,1)但这样似乎不好表示,换个思路,如果两个纹理纵向排列,是不是只用采集uv.x=(0,0.3333)而uv.y=(-0.3333,0)就行了。这样就可以先对y进行-1的一个偏移,此时再对uv.x/=3以及uv.y/=3就能达到我们想要的效果了

这里介绍一下uv的offset,这个就是将uv进行偏移的变量,要使用这个,必须先在vertex顶点函数中进行uv的变换,也就是UNITY_TRANSFER_FOG(o,o.vertex);或者自己书写o.uv.xy = o.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;同时在纹理设置中,需要将WrapMode设置成为Repeat。这个Repeat是用来做什么的呢?是为了当uv超过1时,它会怎样做。这里其实可以想象一下,比如一个九宫格的uv是0到1,如果我们将2个9宫格横向并列放置,那是不是就可以看作uv的x是0到2了,那么在采样中,如果uv超过了1,比如1.2,它是不是就会先采样第一张的0.2到0.8部分,再采样第二张九宫格的前0.2部分。这时如果将uv做一个除法的话,会造成uv的值重新回到了0到1之间,

然后再介绍offset,offset就相当于给原uv进行相加的操作,比如offset.x = 0.2 那就相当于给uv.x加了0.2就与上边一样了

2.序列帧动画的实现--每秒动态变化九宫格内的数字

然后就是实现帧序列动画了,帧序列动画其实就是快速变换采样的uv值,在短时间内采集完一张图集以实现动画的效果。这里直接接着上面的说,如果我们想呈现2,只需将采集uv.x范围为(0.333,0.666),uv.y范围为(-0.333,0)即可此时如何得到这个值,只需将x的偏移+1即可,当我们将x/3时,此时得到的结果就是0 + 1/3到0.3 + 1/3
顺势总结出规律,呈现1的话就是0 + 0/3到 0.333 + 0/3;那我们猜想3是不是就是 0 + 2/3到0.333 + 2/3,确实如我们所想。
我们继续,4的话按照上面的规律,不过这次要偏移y值了,1的话可以写出式子,是0 - 1/3 到 -0.333 -1/3,4的话y值就是0 - 2/3到-0.3333 - 2/3,7的话就是0 - 3/3到 -0.333 - 3/3了。知道原理,剩下的就是在UnityShader中设置对应的属性以及在片元着色器中对uv进行应用就行了
首先定义两个属性 _HorAmount和 _VerAmount以及一个_Speed;
在片元着色其中有

float time = floor(_Time.y * _Speed);//这个可以看作移动的一个速度

float xoffset = time % _HAmount;
float yoffset = floor(time / _HAmount);

float2 uv = i.uv + float2(xoffset, -yoffset);
uv.x /= _HAmount;
uv.y /= _VAmount;


总代码如下

Shader "Unlit/AnimShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _HAmount("HAmount", int) = 3
        _VAmount("VAmount", int) = 3
        _Speed ("Speed", float) = 10
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            int _HAmount;
            int _VAmount;
            float _Speed;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float time = floor(_Time.y * _Speed);

                i.uv.y -= 1;
                
                float xoffset = time % _HAmount;
                float yoffset = floor(time / _HAmount);

                float2 uv = i.uv + float2(xoffset, -yoffset);
                uv.x /= _HAmount;
                uv.y /= _VAmount;
                // sample the texture
                fixed4 col = tex2D(_MainTex, uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

Unity Shader 序列帧动画原理_第1张图片

结束语:这个案例只是介绍序列帧动画的实现原理,如果想实现动画的效果只需改变纹理以及速度值即可

你可能感兴趣的:(Unity,Shader,unity)