Unity Animation Instancing插件的基礎(chǔ)使用

Animation Instancing是Unity在幾年前開源的一個(gè)插件,用于讓人物動(dòng)畫能在GPU中進(jìn)行渲染,從而能夠利用GPU Instancing來提升性能。這個(gè)插件能夠大幅優(yōu)化場(chǎng)景出現(xiàn)大量帶有動(dòng)畫的人物時(shí)的性能表現(xiàn),Unity官方的論壇中也有一篇關(guān)于該插件的介紹文章,有興趣的同學(xué)可以看看。

Animation Instancing:高性能大規(guī)模動(dòng)畫解決方案 - 技術(shù)專欄 - Unity官方開發(fā)者社區(qū)

我也自己測(cè)試了下該插件的實(shí)際性能表現(xiàn),方法是在場(chǎng)景中生成1250個(gè)帶動(dòng)畫的角色。
直接在編輯器中的表現(xiàn)如下:

未使用插件
使用插件

在oppo find x實(shí)機(jī)測(cè)試中的表現(xiàn)如下:

未使用插件 截屏
使用插件 截屏
未使用插件 幀數(shù)
使用插件 幀數(shù)

可以看到,在使用插件下,運(yùn)行的幀數(shù)有巨大的提升。

這個(gè)插件要求運(yùn)行平臺(tái)支持OpenGL ES 3.0及以上,如果你的最低目標(biāo)平臺(tái)無法達(dá)到這個(gè)要求,可能就得放棄這個(gè)插件了。不過如果你的游戲是發(fā)布安卓平臺(tái),安卓早在Android 4.3也就是API等級(jí)18的時(shí)候已經(jīng)加入了對(duì)OpenGL ES 3.0的支持,所以這個(gè)要求對(duì)于現(xiàn)在來說應(yīng)該一點(diǎn)都不過分。
展示了這個(gè)插件的強(qiáng)大之處后,就開始正式講解一下插件的基礎(chǔ)使用步驟和我在使用過程中踩的坑吧。

一、使用流程

1、下載

在Unity的github賬號(hào)上下載:https://github.com/Unity-Technologies/Animation-Instancing

2、導(dǎo)入模型設(shè)置

將模型的Read/Write打開,不然腳本獲取不了模型數(shù)據(jù)。



將Rig設(shè)置為Humanoid。(其它的類型暫時(shí)沒有試過)


3、Prefab制作

給prefab設(shè)置AnimatorController并添加AnimationInstancing腳本。



在菜單欄中打開Animation Generator,將剛才制作的Prefab托進(jìn)去,然后點(diǎn)擊Generate生成動(dòng)畫數(shù)據(jù)。




完成后工程會(huì)出現(xiàn)一個(gè)AnimationTexture文件夾,生成的動(dòng)畫數(shù)據(jù)就在里面。同時(shí)Prefab上的AnimationInstancing腳本里的Prototype也會(huì)附上值。

注意!場(chǎng)景里使用的該P(yáng)refab不能改名,否則無法正常執(zhí)行動(dòng)畫,我猜測(cè)應(yīng)該是和生成的數(shù)據(jù)文件名字對(duì)不上導(dǎo)致的。所以最好是給Prefab外再套一層空父物體,作為最終的Prefab使用。
最后,將物體材質(zhì)修改為插件提供的材質(zhì),并且將材質(zhì)球的Enable GPU Instancing勾選上(如果Enable GPU Instancing沒有勾選上,在安卓平臺(tái)模型會(huì)無法顯示)



4、安卓加載處理

因?yàn)樾枰虞d動(dòng)畫數(shù)據(jù),插件會(huì)在Android平臺(tái)下默認(rèn)要求加載動(dòng)畫數(shù)據(jù)所在的AB包,否則會(huì)拋出錯(cuò)誤。所以在加載Prefab前,需要先調(diào)用插件腳本的AB包加載方法指定AB包路徑。

yield return AnimationManager.Instance.LoadAnimationAssetBundle(Application.streamingAssetsPath + "/AssetBundle/animationtexture");

腳本自身也在菜單欄中提供了一個(gè)將動(dòng)畫數(shù)據(jù)打包的方法。打包之前,需要我們自己手動(dòng)創(chuàng)建StreamingAssets文件夾并在其中建一個(gè)AssetBundle文件夾,不然打包的時(shí)候會(huì)報(bào)錯(cuò)找不到這個(gè)路徑。



打包完成后會(huì)自動(dòng)建一個(gè)AssetBundle文件夾,并且在其中和StreamingAssets/AssetBundle文件夾里生成出動(dòng)畫數(shù)據(jù)專用的AB包。

二、自己編寫shader

插件自身提供了3個(gè)基礎(chǔ)shader可以使用,在插件目錄下的Shader文件夾中。但是這恐怕并不能滿足制作需求,并且無法在URP渲染管線中正常運(yùn)行。所以有必要自己寫個(gè)shader。

1、基礎(chǔ)原理

首先,了解一下插件中shader的核心文件AnimationInstancingBase.cginc。這個(gè)文件就在插件目錄下的Shader文件夾中。AnimationInstancingBase.cginc文件實(shí)現(xiàn)了GPU蒙皮動(dòng)畫的核心邏輯,并且通過vert方法可以很方便的進(jìn)行調(diào)用。

void vert(inout appdata_full v)
{
#ifdef UNITY_PASS_SHADOWCASTER
    v.vertex = skinningShadow(v);
#else
    v.vertex = skinning(v);
#endif
}

整個(gè)蒙皮動(dòng)畫的處理都在頂點(diǎn)著色器內(nèi)進(jìn)行,畢竟其原理就是根據(jù)動(dòng)畫數(shù)據(jù)對(duì)頂點(diǎn)做一系列的變換操作。所以在shader的頂點(diǎn)著色器中調(diào)用vert就能將蒙皮動(dòng)畫的處理邏輯整合進(jìn)自己的shader中。

2、編寫shader

先貼一個(gè)自定義shader的完整代碼。這個(gè)shader只實(shí)現(xiàn)了最基礎(chǔ)的紋理渲染、投射陰影和GPU Instance。

Shader "My/UnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

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

            #include "UnityCG.cginc"
            #include "AnimationInstancingBase.cginc"

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

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vertn (appdata_full v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                vert(v);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }

        Pass
        {
            Tags{"LightMode" = "ShadowCaster"}

            CGPROGRAM

            #pragma vertex vertn
            #pragma fragment frag
            #pragma multi_compile_instancing
            #pragma multi_compile_shadowcaster

            #include "UnityCG.cginc"
            #include "AnimationInstancingBase.cginc"

            struct v2f
            {
                V2F_SHADOW_CASTER;
            };

            v2f vertn(appdata_full v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                vert(v);
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i);
            }

            ENDCG
        }
    }
}

自定義的shader中有兩個(gè)pass塊,第一個(gè)用于渲染物體,第二個(gè)用于投射陰影。

首先,每個(gè)pass塊中需要加入對(duì)AnimationInstancingBase.cginc的引用,并且shader文件需要和AnimationInstancingBase.cginc文件在統(tǒng)一目錄下,這樣才能使用AnimationInstancingBase.cginc中提供的功能。

#include "AnimationInstancingBase.cginc"

然后就是在自己的頂點(diǎn)著色器中調(diào)用AnimationInstancingBase.cginc中提供的vert方法。

v2f vertn (appdata_full v)
{
    v2f o;
    UNITY_SETUP_INSTANCE_ID(v);
    vert(v);
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}
v2f vertn(appdata_full v)
{
    v2f o;
    UNITY_SETUP_INSTANCE_ID(v);
    vert(v);
    TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
    return o;
}

shader中也需要加入gpu instance相關(guān)引用,否則shader無法支持gpu instance,材質(zhì)球的面板里就不會(huì)有Enable GPU Instancing選項(xiàng)。
首先添加編譯指示,表明shader要使用gpu instance。

#pragma multi_compile_instancing

然后由于GPU蒙皮動(dòng)畫的邏輯中需要在頂點(diǎn)著色器里使用多例化屬性變量,所以需要在頂點(diǎn)著色器中添加對(duì)UNITY_SETUP_INSTANCE_ID的調(diào)用。

v2f vertn (appdata_full v)
{
    v2f o;
    UNITY_SETUP_INSTANCE_ID(v);
    vert(v);
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}
v2f vertn(appdata_full v)
{
    v2f o;
    UNITY_SETUP_INSTANCE_ID(v);
    vert(v);
    TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
    return o;
}

本來頂點(diǎn)著色器使用的數(shù)據(jù)結(jié)構(gòu)中也需要添加對(duì)UNITY_VERTEX_INPUT_INSTANCE_ID的引用,但我們頂點(diǎn)著色器使用了Unity內(nèi)置的appdata_full數(shù)據(jù)結(jié)構(gòu),所以就不用管了。

以上,就是我粗淺嘗試了下Animation Instancing插件后整理的基礎(chǔ)使用方法了。更多的操作可以自行查看插件的示例代碼和插件的源碼,比如在AnimationInstancing腳本中可以找到PlayAnimation和CrossFade之類的動(dòng)畫控制方法,也可以在AnimationManager腳本中找到動(dòng)畫數(shù)據(jù)的加載邏輯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、委托是什么,事件是委托嗎? 它們有什么區(qū)別? C#中委托通常是指委托類型創(chuàng)建的對(duì)象,它用于保存和調(diào)用同類型的方...
    SeriousWilson閱讀 2,568評(píng)論 0 1
  • 原文地址:http://blog.sina.com.cn/s/blog_471132920101dcnr.html...
    你的頭好大閱讀 556評(píng)論 0 0
  • 一:什么是協(xié)同程序?答:在主線程運(yùn)行時(shí)同時(shí)開啟另一段邏輯處理,來協(xié)助當(dāng)前程序的執(zhí)行。換句話說,開啟協(xié)程就是開啟一個(gè)...
    CrixalisAs閱讀 2,245評(píng)論 1 7
  • Unity 多例化技術(shù) 什么是多例化技術(shù) 使用 GPU 多例化可使用少量繪制調(diào)用一次繪制(或渲染)同一網(wǎng)格的多個(gè)副...
    BacteriumFox閱讀 1,447評(píng)論 0 0
  • 111. [動(dòng)畫系統(tǒng)]如何將其他類型的動(dòng)畫轉(zhuǎn)換成關(guān)鍵幀動(dòng)畫? 動(dòng)畫->點(diǎn)緩存->關(guān)鍵幀 112. [動(dòng)畫]Unit...
    胤醚貔貅閱讀 13,563評(píng)論 3 88

友情鏈接更多精彩內(nèi)容