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

相关文章
相关标签/搜索