URP下GPU Instance以及IndirectDraw探究


今天一個渲染群群友提出了在URP下無法IndirectDraw的問題,直接使用的官方文檔的代碼。我們知道DrawMeshInstancedIndirect方法在官方文檔上有兩個支持的shader,一個是表面着色器,一個是頂點片元着色器。而URP下不支持表面着色器,所以群友把頂點片元着色器拿到了URP下使用,結果發現DrawIndirect並不能正確執行。問題出在了哪里呢?

對於想快速得到答案的小伙伴們,這里先給出結論:

首先必須要加這一句預編譯指令:

#pragma instancing_options procedural:setup

setup方法里面是用來處理InstanceID計算出來后去怎么處理數據,如果用不到可以不填充這個方法,但是必須實現。

void setup()
            {

            }

如上,空着就可以,這個預編譯指令主要是用來告訴unity我們需要drawIndirect。

然后不能直接從SV_InstanceID中拿instanceID,必須先定義在頂點的輸入和輸出結構中。

之前錯誤的代碼是這樣的:

 Varyings vert(Attributes v,uint instanceID: SV_InstanceID)
            {
                xxxxx
            }

而正確的做法是這樣的:

struct Attributes
            {
                float3 positionOS : POSITION;
                float2 uv :TEXCOORD0;
        UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            // 頂點着色器的輸出
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv :TEXCOORD0;
            UNITY_VERTEX_INPUT_INSTANCE_ID
            };
Varyings vert(Attributes v)
            {
                Varyings o = (Varyings)0;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                xxxxxxxxxxxxxxxxxxxxx
                return o;
            }

然后要獲取instanceID可以通過unity_InstanceID拿到。

結論講完說一下原因:

首先第一個預編譯指令存在的意義是如果我們只有:

#pragma multi_compile_instancing

當IndirectDraw的時候你會發現UNITY_INSTANCING_ENABLED這個keyword是關閉的。而正常情況下用Renderer渲染時是打開的。為什么會這樣呢?因為Unity把這兩種渲染方式做了區分。

使用Renderer渲染時當開啟GPU Instancing,會激活UNITY_INSTANCING_ENABLED,但是當DrawIndirect的時候,Unity希望我們激活的是UNITY_PROCEDURAL_INSTANCING_ENABLED這個keyword,所以會把UNITY_INSTANCING_ENABLED關閉掉。那么如果我們沒有定義上面instancing_options procedural:setup的預編譯指令,連UNITY_PROCEDURAL_INSTANCING_ENABLED也不會被激活,所以就無法Instance了。這是為什么必須要這個預編譯指令。

那么這個預編譯指令后面的setup方法是用來干什么呢?他提供了一個當InstanceID計算完成后處理數據的階段。比如如何通過InstanceID去取數據之類的。

第二個點是為什么不能在方法參數里面直接使用SV_InstanceID的語義,而必須定義在結構里面呢?

這個就是內置管線和URP的一點區別,目前具體的原因還不太清楚,但是一般我們讓自己的shader支持GPU Instance都是通過手動SETUP進行的。

最后就是關於UNITY_PROCEDURAL_INSTANCING_ENABLED這個宏:

這個就得看UnityInstancing.hlsl這個文件里面對於UNITY_GET_INSTANCE_ID這個宏的定義:

 #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
        #ifndef UNITY_INSTANCING_PROCEDURAL_FUNC
            #error "UNITY_INSTANCING_PROCEDURAL_FUNC must be defined."
        #else
            void UNITY_INSTANCING_PROCEDURAL_FUNC(); // forward declaration of the procedural function
            #define DEFAULT_UNITY_SETUP_INSTANCE_ID(input)      { UnitySetupInstanceID(UNITY_GET_INSTANCE_ID(input)); UNITY_INSTANCING_PROCEDURAL_FUNC();}
        #endif
    #else
        #define DEFAULT_UNITY_SETUP_INSTANCE_ID(input)          { UnitySetupInstanceID(UNITY_GET_INSTANCE_ID(input));}
    #endif

可以看到這個宏只能在setup方法里使用,不能在其他方法比如頂點片元使用,那么當我們想判斷是否是Instance的情況時,可以使用這個宏來判斷:

UNITY_ANY_INSTANCING_ENABLED

這個宏是可以用在頂點片元着色器的,只要兩種Instance有一個激活了,這個宏就會返回1.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM