[UnityShader入门精要读书笔记]25.纹理动画

冷暖自知 提交于 2020-01-14 02:00:58

1.序列帧动画

          最常见的纹理动画之一就是序列帧动画。序列帧动画的原理非常简单。它像放电影一样,依次播放一系列关键帧图像,当播放速度达到一定数值时,看起来就是一个连续的动画。它的有点在于灵活性很强。我们不需要进行任何物理计算就可以得到很好的动画效果。

Shader "Unlit/ImageSequenceAnimation"
{
    Properties
    {
        //包含了所有关键帧图像的纹理。
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color Tint", Color) = (1,1,1,1)
        //分别代表该图像在水平方向和竖直方向包含的关键帧图像的个数。
        _HorizontalAmount("HorizontalAmount",Float) = 4
        _VerticalAmount("VerticalAmount", Float) = 4
        //控制序列帧动画播放速度
        _Speed("Speed",Range(1,100)) = 30
    }
    SubShader
    {
        //由于序列帧图像通常包含透明通道,因此可以被当成是一个半透明对象。在这里我们使用半透明的“标配”来设置它的SubShader标签,即把Queue和RenderType设置成Transparent,把IgnoreProject设置为True。在Pass中,我们使用Blend命令来开启并设置混合模式,同时关闭深度写入。

        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            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;
            fixed4 _Color;
            float _Speed;
            float _HorizontalAmount;
            float _VerticalAmount;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            //要播放序列帧,从本质上来说,我们需要计算出每个时刻需要播放的关键帧在纹理中的位置。而由于序列帧纹理都是按列排列的,因此这个位置可以认为是该关键帧所在的行列索引数。
            fixed4 frag (v2f i) : SV_Target
            {
                //前3行代码计算了行列数,其中使用了Unity的内置时间变量_Time。由于_Time.y就是自该场景加载后所经过的时间。我们首先把_Time.y和速度属性_Speed相乘来得到模拟的时间,并使用CG的floor函数对结果值取整数时间time。然后,我们使用time除以_HorizontalAmount的结果值的商来作为当前对应的行索引,除法结果的余数则是列索引。接下来,我们需要使用行列索引值来构建真正的采样坐标。由于序列帧图像包含了许多关键帧图像,这意味着采样坐标需要映射到每个关键帧图像的坐标范围内。我们可以首先把原纹理坐标i.uv按行数和列数进行等分,得到每个子图像的纹理坐标范围。然后,我们需要使用当前的行列数对上面的结果进行偏移,得到当前子图像的纹理坐标。需要注意的是,对竖直方向的坐标便宜需要使用减法,这是因为在Unity中纹理坐标竖直方向的顺序和序列帧纹理中的顺序是相反的。
                float time = floor(_Time.y * _Speed);
                float row = floor(time / _HorizontalAmount);
                float column = time - row * _VerticalAmount;
                half2 uv = i.uv + half2(column, -row);
                uv.x /= _HorizontalAmount;
                uv.y /= _VerticalAmount;

                fixed4 col = tex2D(_MainTex, uv);
                col.rgb *= _Color;
                return col;
            }
            ENDCG
        }
    }
}
 

滚动的背景

很多2D游戏都是用了不断滚动的北京来模拟游戏角色在场景中的穿梭,这些背景往往包含了多个层来模拟一种视差效果。而这些背景的实现往往就是利用了纹理动画。

Shader "Unlit/scrollingbg"

{

    Properties

    {

        //第一层背景纹理

        _MainTex ("Texture", 2D) = "white" {}

        //第二层背景纹理

        _DetailTex ("2nd Layer (RGB)",2D) = "white" {}

        //水平滚动速度

        _ScrollX ("Base layer Scroll Speed", Float) = 1.0

        _Scroll2X ("2nd layer Scroll Speed", Float) = 1.0

        //控制纹理整体亮度

        _Multiplier ("Layer Multiplier", Float) = 1

    }

    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 texcoord : TEXCOORD0;

            };

 

            struct v2f

            {

                float2 uv : TEXCOORD0;

                float4 pos : SV_POSITION;

            };

 

            sampler2D _MainTex;

            float4 _MainTex_ST;

 

            v2f vert (appdata v)

            {

                v2f o;

                //首先进行顶点变换,把顶点从模型空间变换到裁剪空间中。然后,我们计算两层纹理背景的纹理坐标。为此,我们首先利用TRANSFORM_TRANSFORM_TEX来得到初始的纹理坐标。然后,利用_Time.y变量在水平方向对纹理坐标进行偏移,我们吧两张纹理坐标存储在同一个变量o.uv中,以减少占用的插值寄存器空间。

                o.pos = UnityObjectToClipPos(v.vertex);

                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex) + frac(float2(_ScrollX, 0.0) * _Time.y);

                o.uv.zw = TRANSFORM_TEX(v.texcoord, _DetailTex) + frac(float2(_Scroll2X, 0.0) * _Time.y);

                return o;

            }

 

            fixed4 frag (v2f i) : SV_Target

            {

                //我们首先分别利用i.uv.xy和i.uv.zw对两张背景纹理进行采样。然后,使用第二层纹理的透明通道来混合两张纹理。最后,使用_Multiplier参数和输出颜色进行相乘,以调整背景亮度。

                fixed4 firstLayer = tex2D(_MainTex, i.uv.xy);

                fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);

                fixed4 c = lerp(firstLayer,secondLayer,secondLayer.a);

                c.rgb *= _Multiplier;

                return c;

            }

            ENDCG

        }

    }

}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!