在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...