美文网首页
Unity Shader: 一个简单的(规则化)序列帧动画(基础

Unity Shader: 一个简单的(规则化)序列帧动画(基础

作者: Danny_Yan | 来源:发表于2022-01-08 22:30 被阅读0次

    规则化序列帧动画是指: 每帧宽高一致,仅通过行列即可表示信息.

    源码如下:

    // 规则化序列帧播放,每帧大小应该一致
    // @author Danny_Yan
    Shader "Test/SimpleMovieClip"
    {
        Properties
        {
            _MainTex ("Image Sequence", 2D) = "white" { }// 序列帧图片
            _RowCount ("行", Float) = 1 // 行数
            _ColumnCount ("列", Float) = 1 // 列数
            _FrameRate ("帧率", Range(1, 100)) = 30 
        }
        SubShader
        {
            //一般序列帧动画的纹理会带有Alpha通道,因此要按透明效果渲染,需要设置标签,关闭深度写入,使用并设置混合
            Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True"}
    
            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;
    
                sampler2D _InfoTex;
                float4 _InfoTex_ST;
    
                fixed4 _Color;
                float _RowCount;
                float _ColumnCount;
                float _FrameRate;
    
                float _Total;
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    // 是用原始uv,不进行平铺和偏移
                    // o.uv = v.uv.xy;// * _MainTex_ST.xy + _MainTex_ST.zw;
    
                    UNITY_TRANSFER_FOG(o,o.vertex);
                    return o;
                }
    
                fixed4 frag (v2f i) : SV_Target
                {
                    // 将时间取整(变成以秒为单位)相当于1秒1帧,放大到_FrameRate后,相当于得到帧index,通过index去计算行列索引.
                    // 必须将纹理的wrap mode设置为Repeat(或类似的设定),因为当time>_ColumnCount*2时,row会大于_RowCount
                    // uvoff中计算的y值会大于1,需要通过纹理的Repeat机制来重复显示.
                    // 或者在外部维护一个index变量,并传进来,这样可以在外层将这个index进行重置为0
                    float index = floor(_Time.y * _FrameRate); 
                    // 取整得到行索引(播放顺序设计为从左到右,先行后列)
                    float rowIndex = floor(index / _ColumnCount);
                    // 余数为列索引 
                    float columnIndex = fmod(index, _ColumnCount); // index - rowIndex * _ColumnCount;
                    
                    half2 iuv = i.uv.xy; // /_MainTex_ST.xy;
                    // 使用中的行列值作为分割计算的元值(总比值). 相当于一个窗口,通过该窗口的上下左右定位得到每帧图片的uv
                    half2 rawSplit = half2(_ColumnCount, _RowCount);
                    // 当前uv通过rawSplit分割后,得到当前uv在总uv中的占比. 相当于(窗口的)固定大小
                    iuv /= rawSplit;
                    // 通过当前计算出的行列值与总比值的比例,得到uv的起始偏移量. 相当于(窗口的)起始位置, row是从上到下,取反后转换为uv的从下到上
                    half2 uvoff = half2(columnIndex, -rowIndex)/rawSplit;
                    iuv += uvoff;
                    
                    // iuv*=-1;
                    fixed4 col = tex2D(_MainTex, iuv);
                    
                    // apply fog
                    UNITY_APPLY_FOG(i.fogCoord, col);
                    return col;
                }
                ENDCG
            }
        }
    
        FallBack "Transparent/VertexLit"
    }
    
    

    为方便观察,制作了一个序列帧测试图:


    image.png

    效果如下:


    效果.gif

    有时我们会在游戏中进行简单的方阵显示,此时会联想到直接使用tiling来平铺显示,但如果此时设置Tiling,会发现在方阵中的其他列(或行,根据你实际是按行优先还是列优先)有出现跳帧的情况,如下图:



    上图中(右侧数字是平铺出来的),当左侧数字变为4的时候,右侧数字变为了0,左侧为9时,右侧变为了5,因为在这2个时刻,columnIndex被重置为了0,但平铺列还未显示到最后一个列索引.

    可以将序列帧处理为单行(会限制帧数量):


    序列帧测试图2.png

    效果如下:


    效果2.gif

    上述方式只能利用一个单行的序列帧合集,实际情况中我们更多的是多个动作序列帧图片做成图集,所以可以用单行来表示一个action,通过指定行索引来切换action.
    如下图所示,数字action和字母action合并为了一个图集,且有各自的帧数量:


    序列帧测试图3.png

    修改后的代码如下:

    Shader "Test/SimpleMovieClip2"
    {
        Properties
        {
            _MainTex ("Image Sequence", 2D) = "white" { }// 序列帧图片
    
            _RowCount ("最大行数", Float) = 1 // 行数
            _ColumnCount ("最大列数", Float) = 1 // 列数
            _FrameRate ("帧率", Range(1, 100)) = 30 // speed
            
            _ActionRowIndex ("action索引", Range(0, 100)) = 0
            _ActionFrames("当前action帧数", Range(0, 100)) = 0
        }
        SubShader
        {
            //一般序列帧动画的纹理会带有Alpha通道,因此要按透明效果渲染,需要设置标签,关闭深度写入,使用并设置混合
            Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True"}
    
            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;
    
                float _RowCount;
                float _ColumnCount;
                float _FrameRate;
    
                float _ActionRowIndex;
                float _ActionFrames;
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    // o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    // 是用原始uv,不进行平铺和偏移
                    o.uv.xy = v.uv.xy;// * _MainTex_ST.xy + _MainTex_ST.zw;
    
                    UNITY_TRANSFER_FOG(o,o.vertex);
                    return o;
                }
    
                fixed4 frag (v2f i) : SV_Target
                {
                    // 将时间取整(变成以秒为单位)相当于1秒1帧,放大到_FrameRate后,相当于得到帧index,通过index去计算行列索引.
                    // 必须将纹理的wrap mode设置为Repeat(或类似的设定),因为当time>_ColumnCount*2时,row会大于_RowCount
                    // uvoff中计算的y值会大于1,需要通过纹理的Repeat机制来重复显示.
                    // 或者在外部维护一个index变量,并传进来,这样可以在外层将这个index进行重置为0
                    float index = floor(_Time.y * _FrameRate);
    
                    // 指定行
                    float rowIndex = _ActionRowIndex;//floor(index / _ColumnCount);
                    // 余数为列索引 
                    float columnIndex = fmod(index, _ActionFrames); // index - rowIndex * _ColumnCount;
                    
                    half2 iuv = i.uv.xy; // /_MainTex_ST.xy;
                    // 使用中的行列值作为分割计算的元值(总比值). 相当于一个窗口,通过该窗口的上下左右定位得到每帧图片的uv
                    half2 rawSplit = half2(_ColumnCount, _RowCount);
                    // 当前uv通过rawSplit分割后,得到当前uv在总uv中的占比. 相当于(窗口的)固定大小
                    iuv /= rawSplit;
                    // 通过当前计算出的行列值与总比值的比例,得到uv的起始偏移量. 相当于(窗口的)起始位置, row是从上到下,取反后转换为uv的从下到上
                    half2 uvoff = half2(columnIndex, -rowIndex)/rawSplit;
                    iuv += uvoff;
                    
                    // iuv*=-1;
                    fixed4 col = tex2D(_MainTex, iuv);
    
                    // apply fog
                    UNITY_APPLY_FOG(i.fogCoord, col);
                    return col;
                }
                ENDCG
            }
        }
    
        FallBack "Transparent/VertexLit"
    }
    

    同时显示2个action效果如下(通过2个Material分别设置相关参数进行处理):


    效果3.gif

    对于大量序列帧动画的性能处理,参考下篇: Unity Shader: 一个简单的(规则化)序列帧动画(性能处理)

    转载请注明出处: https://www.jianshu.com/p/6946971c22f8

    参考:
    https://developpaper.com/unity-shader-realizes-the-effect-of-sequential-frame-animation/

    相关文章

      网友评论

          本文标题:Unity Shader: 一个简单的(规则化)序列帧动画(基础

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