在Unity中 同網格同材質的模型是能夠合批的
動態批處理和靜態批處理均可以合批 可是都有其限制
動態批處理有頂點數不能超過900的限制 只適合比較簡單的模型
靜態批處理的物體不能移動、旋轉、縮放 而且須要消耗額外的內存來存儲合併後的物體html
若是動態靜態批處理都沒法使用 可否用其餘方式合批呢?
能夠嘗試一下GPU Instance 雖然也有所限制 可是提供了更多可能segmentfault
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 Instancing確實能夠動態合批 可是須要Shader + 硬件 + 代碼的支持
好處是能夠解決動態批處理解決不了的問題 沒900頂點的限制性能
ps:雖然官方的Standard Shader提供了GPU Instance的選項 可是給材質設置不一樣顏色後合批失敗了 這裏有坑 使用了其餘Shader後解決學習
測試效果:
測試
測試代碼:優化
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 速度快性能高
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...