GPU Animation-03-运行

301 阅读2分钟

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。

作为一名优秀的开发者,对于技术的探求应该是永无止步的,如何让自己更进步,那就只有不停的学习,不停的充电.而这些都都是说起来容易做起来难,坚持才是难的地方 --蛙哈哈

上文我们实现了如果将一个正常的一个带SkinnedMeshRenderer组件的角色,装换为一个可以执行GPU Animation的角色.

这篇文章咱们讲一下转换后的角色到底是怎样通过GPU 播放动画的.

贴代码:

Shader "chenjd/BuiltIn/AnimMapShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_AnimMap ("AnimMap", 2D) ="white" {}
		_AnimLen("Anim Length", Float) = 0
	}
	
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100
        Cull off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //开启gpu instancing
            #pragma multi_compile_instancing

            #include "UnityCG.cginc"

            struct appdata
            {
                float2 uv : TEXCOORD0;
                float4 pos : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _AnimMap;
            float4 _AnimMap_TexelSize;//x == 1/width

            float _AnimLen;

            
            v2f vert (appdata v, uint vid : SV_VertexID)
            {
                UNITY_SETUP_INSTANCE_ID(v);

                float f = _Time.y / _AnimLen;

                fmod(f, 1.0);

                float animMap_x = (vid + 0.5) * _AnimMap_TexelSize.x;
                float animMap_y = f;

                float4 pos = tex2Dlod(_AnimMap, float4(animMap_x, animMap_y, 0, 0));

                v2f o;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.vertex = UnityObjectToClipPos(pos);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
	}
}

UNITY_VERTEX_INPUT_INSTANCE_ID 在之前的文章中讲过了就是告诉渲染器可以知道当前渲染的是哪个实例,并且可以通过UNITY_SETUP_INSTANCE_ID(v);方法获取到当前实例存储到缓冲区的数据,就可以对不同的实例进行个性化的操作.

float f = _Time.y / _AnimLen;

fmod(f, 1.0);

fmod 返回a / b的浮点余数.

这句就是通过当前运行的时间/动画的长度,再进行fmod, 就可以得到当前这一帧率, 应该播放动画的哪一帧.

float animMap_x = (vid + 0.5) * _AnimMap_TexelSize.x;

{TextureName}_TexelSize 是一个四元数,是 unity 内置的变量,它的值为 Vector4(1 / width, 1 / height, width, height),它的作用就是快速的取到这个贴图的Size数据.

_AnimMap_TexelSize.x 就是表示的是 _AnimMap 这张贴图的1/width,就正好能取到每个顶点的所在的横坐标,再加上这一帧的位置, 就是上面的f.

就正好能取到当前帧当前顶点所在的坐标,这个坐标里面的颜色值,就是这个mesh顶点应该在的位置.

关于(vid + 0.5) 这句话我这种小菜菜也是看不太懂,我就在这个项目的issues中找到了大神的解答.

image-20220126222026135.png

float4 pos = tex2Dlod(_AnimMap, float4(animMap_x, animMap_y, 0, 0));

o.vertex = UnityObjectToClipPos(pos);

拿到当前顶点的位置,并将顶点的设置在正确的位置,即可.

这样随意时间的推进,就可以循环播放角色的动画啦!