Unity GPU Instance(大量相同網格物體合批)

前言

在Unity中 同網格同材質的模型是能夠合批的
動態批處理和靜態批處理均可以合批 可是都有其限制
動態批處理有頂點數不能超過900的限制 只適合比較簡單的模型
靜態批處理的物體不能移動、旋轉、縮放 而且須要消耗額外的內存來存儲合併後的物體html

若是動態靜態批處理都沒法使用 可否用其餘方式合批呢?
能夠嘗試一下GPU Instance 雖然也有所限制 可是提供了更多可能segmentfault

使用GPU Instancing的條件

1.Shader支持GPU Instancing
2.硬件支持GPI Instancing
3.代碼動態繪製物體app

硬件需求:dom

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)

限制狀況:ide

下列狀況不能使用Instancing:
·使用Lightmap的物體
·受不一樣Light Probe / Reflection Probe影響的物體
·使用包含多個Pass的Shader的物體,只有第一個Pass能夠Instancing前向渲染時,
受多個光源影響的物體只有Base Pass能夠instancing,Add Passes不行

GPU Instance測試

GPU Instancing確實能夠動態合批 可是須要Shader + 硬件 + 代碼的支持
好處是能夠解決動態批處理解決不了的問題 沒900頂點的限制性能

ps:雖然官方的Standard Shader提供了GPU Instance的選項 可是給材質設置不一樣顏色後合批失敗了 這裏有坑 使用了其餘Shader後解決學習

測試效果:
GPU Instancing合批
MaterialPropertyBlock測試

測試代碼:優化

using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// PropertyBlockTest
/// ZhangYu 2019-06-17
/// <para>Blog:https://segmentfault.com/a/1190000019553301</para>
/// </summary>
public class PropertyBlockTest : MonoBehaviour {

    public GameObject prefab;
    public int count = 100;
    private Mesh insMesh;
    private Material insMaterial;
    private List<Matrix4x4> insMatrices;
    private MaterialPropertyBlock insBlock;
    private List<Color> insColors;
    private int colorID;

    private void Start () {
        GPUInstanceByBlock();
        //GPUInstanceByDrawMesh();
    }

    private void Update() {
        if (insMesh != null) DrawMeshes();
    }

    // 方法1:經過Shader + PropertyBlock 實現GPU Instance
    private void GPUInstanceByBlock() {
        MaterialPropertyBlock block = new MaterialPropertyBlock();
        int colorID = Shader.PropertyToID("_Color");
        GameObject[] objs = new GameObject[count];
        for (int i = 0; i < count; i++) {
            Vector3 position = new Vector3(Random.Range(-8, 8f), Random.Range(-4, 4f), 3);
            Color color = new Color(Random.Range(0, 1f), Random.Range(0, 1f), Random.Range(0, 1f));
            block.SetColor(colorID, color);
            GameObject obj = Instantiate(prefab);
            // 用Block代替Material設置值 這樣就能合批了
            obj.GetComponent<MeshRenderer>().SetPropertyBlock(block);
            obj.transform.position = position;
            obj.SetActive(true);
        }
    }

    // 方法2:經過DrawMesh + Shader + PropertyBlock實現GPU Instance
    private void GPUInstanceByDrawMesh() {
        insMesh = prefab.GetComponent<MeshFilter>().mesh;
        insMaterial = prefab.GetComponent<Renderer>().material;
        insMatrices = new List<Matrix4x4>();
        insColors = new List<Color>();
        insBlock = new MaterialPropertyBlock();
        colorID = Shader.PropertyToID("_Color");
        for (int i = 0; i < count; i++) {
            Vector3 position = new Vector3(Random.Range(-8, 8f), Random.Range(-4, 4f), 3);
            Quaternion rotation = prefab.transform.rotation;
            Color color = new Color(Random.Range(0, 1f), Random.Range(0, 1f), Random.Range(0, 1f));
            // Position + Rotation + Scale > Matrix4x4
            Matrix4x4 matrix = TransformToMatrix(position, rotation);
            insMatrices.Add(matrix);
            insColors.Add(color);
        }
    }

    private void DrawMeshes() {
        // 測試結果:
        // 同網格 同材質 能夠合批 須要Shader支持GPU Instance + 用PropertyBlock設置參數

        // DrawMeshInstanced() 一次繪製多個物體 調用一次 一個DrawCall
        //Graphics.DrawMeshInstanced(insMesh, 0, insMaterial, insMatrices, insBlock);

        // DrawMesh() 一次繪製一個物體 屢次調用 能夠合成一批
        for (int i = 0; i < count; i++) {
            insBlock.SetColor(colorID, insColors[i]);
            Graphics.DrawMesh(insMesh, insMatrices[i], insMaterial, 1, Camera.main, 0, insBlock);
        }
    }

    private Matrix4x4 TransformToMatrix(Vector3 position) {
        return Matrix4x4.TRS(position, Quaternion.identity, Vector3.one);
    }

    private Matrix4x4 TransformToMatrix(Vector3 position, Quaternion rotation) {
        return Matrix4x4.TRS(position, rotation, Vector3.one);
    }

    private Matrix4x4 TransformToMatrix(Vector3 position, Quaternion rotation, Vector3 scale) {
        return Matrix4x4.TRS(position, rotation, scale);
    }

}

測試Shader:ui

Shader "SimplestInstancedShader"
{
    Properties
    {
        _Color("Color", Color) = (1, 1, 1, 1)
    }

        SubShader
    {
        Tags{ "RenderType" = "Opaque" }
        LOD 100

        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"

    struct appdata
    {
        float4 vertex : POSITION;
        UNITY_VERTEX_INPUT_INSTANCE_ID
    };

    struct v2f
    {
        float4 vertex : SV_POSITION;
        UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in fragment Shader.
    };

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
        UNITY_INSTANCING_BUFFER_END(Props)

        v2f vert(appdata v)
    {
        v2f o;

        UNITY_SETUP_INSTANCE_ID(v);
        UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.

        o.vertex = UnityObjectToClipPos(v.vertex);
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
        return  UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
    }
        ENDCG
    }
    }
}

MaterialPropertyBlock的說明:
材質屬性塊被用於Graphics.DrawMesh和Renderer.SetPropertyBlock兩個API,當咱們想要繪製許多相同材質但不一樣屬性的對象時可使用它。例如你想改變每一個繪製網格的顏色,可是它卻不會改變渲染器的狀態。
簡單來講利用PropertyBlock設置屬性不會產生新的Mesh和Material 速度快性能高

SkinMeshRender的GPU Instancing

Unity官方開源的Animation Instacing: https://blogs.unity3d.com/cn/...

CSDN博主
《Unity中使用GPU Instancing優化SkinnedMesh渲染》https://blog.csdn.net/xoyojan...

參考資料:
《[unity]GPU Instance學習》:https://www.jianshu.com/p/ecf...
《使用MaterialPropertyBlock來替換Material屬性操做》 https://blog.uwa4d.com/archiv...
《Unity3D研究院GPU Instancing實戰(九十七)》https://www.xuanyusong.com/ar...

轉載請標明原文地址:https://segmentfault.com/a/11...

相關文章
相關標籤/搜索