Unity GPU Instancing的使用嘗試


似乎是在Unity5.4中開始支持GPU Instacing,但如果要比較好的使用推薦用unity5.6版本,因為這幾個版本一直在改。

這里測試也是使用unity5.6.2進行測試

在5.6的版本里,Instancing不再是一個單獨的shader,而是一個開關。

如果使用Instancing功能需要開啟,否則調用相關接口時會報錯

 

默認情況下,多個一樣的模型會被動態批次合並優化掉,動態批次合並有很多種情況不可用,其中一種就是鏡像的情況。

這里用鏡像后的實例模型和GPU Instancing做比較

 

注意,在Unity5.6.2或者之后的版本中,只要材質球勾選Instancing,即自動開啟並使用GPU Instancing。

 

GPU Instancing大致代碼如下(用Graphics一次性調用減少了層級對象的創建開銷):

void Update()
{
    var meshRenderer = template.GetComponent<MeshRenderer>();
    var meshFilter = template.GetComponent<MeshFilter>();
    var mesh = meshFilter.sharedMesh;
    var matrices = new Matrix4x4[instanceCount];

    for (int i = 0; i < matrices.Length; i++)
    {
        var position = Random.insideUnitSphere * range;
        var rotation = Quaternion.LookRotation(Random.insideUnitSphere);
        var scale = Vector3.one * Random.Range(-2f, 2f);
        var matrix = Matrix4x4.TRS(position, rotation, scale);

        matrices[i] = matrix;
    }

    Graphics.DrawMeshInstanced(mesh, 0, meshRenderer.sharedMaterial, matrices);
}

 

是實時隨機的位置,會看見只有13個Batches.

 

 

常規實例化測試腳本(掛了正弦運動腳本,注意鏡像反轉,使其無法動態批次合並):

for (int i = 0; i < instanceCount; i++)
{
    var instancedTemplate = Instantiate(template);
    instancedTemplate.transform.position = Random.insideUnitSphere * range;
    instancedTemplate.transform.forward = Random.insideUnitSphere;
    instancedTemplate.transform.localScale = Vector3.one * Random.Range(-2f, 2f);
}

 

大概在1020個Batches

 

 

 另外我還打了個APK包測了下,居然能在我的紅米3S上跑。這就有點厲害了

 

 

那么GPU Instacing其實也有一些限制的,比如不支持蒙皮網格等(不過資源商店有一個Animation Instacing: 鏈接)

(補充:Unity官方開源了一個Animation Instacing: https://blogs.unity3d.com/cn/2018/04/16/animation-instancing-instancing-for-skinnedmeshrenderer/)

 

這些支持信息在官網的頁面都有羅列 https://docs.unity3d.com/Manual/GPUInstancing.html

 

硬件需求:

GPU Instancing is available on the following platforms and APIs:

  • DirectX 11 and DirectX 12 on Windows

  • OpenGL Core 4.1+/ES3.0+ on Windows, macOS, Linux, iOS and Android

  • Metal on macOS and iOS

  • Vulkan on Windows and Android

  • PlayStation 4 and Xbox One

  • WebGL (requires WebGL 2.0 API)

 

模塊間的需求(沒找到原版的帖子,翻譯版摘抄一段):

下列情況不能使用Instancing:

  • 使用Lightmap的物體
  • 受不同Light Probe / Reflection Probe影響的物體
  • 使用包含多個Pass的Shader的物體,只有第一個Pass可以Instancing前向渲染時,受多個光源影響的物體只有Base Pass可以instancing,Add Passes不行

 

 

另外,默認的DrawMeshInstanced有1024實例數的限制

需要DrawMeshInstancedIndirect,而這個接口依賴ComputerShader,一些平台不支持。

 

 

然后再測一下GPU Instanced Indirect,也就是DrawMeshInstancedIndirect這個接口

似乎是借助ComputerShader實現超過1024數量的Instancing

下圖為3萬個Cube:

 

代碼和shader我做了點修改,大致如下:

void Update()
{
    // Update starting position buffer
    if (cachedInstanceCount != instanceCount) UpdateBuffers();

    for (int i = 0; i < mPositions.Length; i++)
    {
        float angle = Random.Range(0.0f, Mathf.PI * 2.0f);
        float distance = Random.Range(20.0f, 100.0f);
        float height = Random.Range(-2.0f, 2.0f);
        float size = Random.Range(0.05f, 0.25f);
        mPositions[i] = new Vector4(Mathf.Sin(angle) * distance, height, Mathf.Cos(angle) * distance, size);
    }

    positionBuffer.SetData(mPositions);
    instanceMaterial.SetBuffer("positionBuffer", positionBuffer);

    // Render
    Graphics.DrawMeshInstancedIndirect(
        instanceMesh,
        0,
        instanceMaterial,
        new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)),
        argsBuffer);
}

 

shader需要額外定制,這點比較蛋疼。如果換成standard讀不到positionBuffer這種結構。

 

DrawMeshInstancedIndirect的具體使用案例,可以參考這兩個鏈接:

https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedIndirect.html

https://github.com/tiiago11/Unity-InstancedIndirectExamples

 

 

補充測試工程地址https://gitee.com/Hont/GPUInstancingTest

(unity2017.4)

 

 

擴展閱讀:

 

Geometry instancing

https://en.wikipedia.org/wiki/Geometry_instancing

 

Unity中的批處理優化與GPU Instancing

http://forum.china.unity3d.com/thread-22714-1-3.html

 


免責聲明!

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



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