Legacy Documentation: Version 5.4
Graphics Command Buffers
Sparse Textures

GPU Instancing

Introduction

You can use GPU instancing to draw many identical objects with only a few draw calls. There are some restrictions which you need to bear in mind:

  • Your identical objects need to share the same mesh and the same material. You can, however, add per-instance data; see Adding per-instance data, below, for more information.
  • The MeshRenderer component and Graphics.DrawMesh API are supported.
  • GPU instancing is available on the following platforms:
    • Windows: DX11 / DX12 with SM 4.0 and above; OpenGL 4.1 and above
    • OS X & Linux: OpenGL 4.1 and above
    • PlayStation 4
    • Xbox One

Adding instancing to your objects

There is a Standard Surface Shader available that supports instancing. Add one to your project by selecting Shader > Standard Surface Shader (Instanced).

Adding the Standard Instanced Shader
Adding the Standard Instanced Shader

Apply this shader to your GameObject’s Material. In your Material’s Inspector window, click the Shader drop-down, roll over the Instanced field and choose your instanced shader from the list:

Assigning the Standard Instanced Shader to a Material
Assigning the Standard Instanced Shader to a Material

Adding per-instance data

Even though the instanced objects are sharing the same mesh and material, you can set shader properties on a per-object basis using the MaterialPropertyBlock API. In the example below, each object is assigned a random color value using the _Color property:

MaterialPropertyBlock props = new MaterialPropertyBlock();
MeshRenderer renderer;

foreach (GameObject obj in objects)
{
   float r = Random.Range(0.0f, 1.0f);
   float g = Random.Range(0.0f, 1.0f);
   float b = Random.Range(0.0f, 1.0f);
   props.SetColor("_Color", new Color(r, g, b));
   
   renderer = obj.GetComponent<MeshRenderer>();
   renderer.SetPropertyBlock(props);
}

Adding instancing to your own shaders

Let’s take a simple unlit shader and make it capable of instancing:

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_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                UNITY_INSTANCE_ID
            };

            UNITY_INSTANCING_CBUFFER_START (MyProperties)
            UNITY_DEFINE_INSTANCED_PROP (float4, _Color)
            UNITY_INSTANCING_CBUFFER_END
           
            v2f vert (appdata v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID (v);
                UNITY_TRANSFER_INSTANCE_ID (v, o);

                o.vertex = UnityObjectToClipPos (v.vertex);
                return o;
            }
           
            fixed4 frag (v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID (i);
                return UNITY_ACCESS_INSTANCED_PROP (_Color);
            }
            ENDCG
        }
    }
}

Added code

Addition: Function:
#pragma multi_compile_instancing multi_compile_instancing generates a shader with two variants: one with built-in keyword INSTANCING_ON defined (allowing instancing), the other with nothing defined. This allows the shader to fall back to a non-instanced version if instancing isn’t supported on the GPU.
UNITY_INSTANCE_ID This is used in the vertex shader input/output structure to define an instance ID. See SV_InstanceID for more information.
UNITY_INSTANCING_CBUFFER_START(name) / UNITY_INSTANCING_CBUFFER_END Every per-instance property must be defined in a specially named constant buffer. Use this pair of macros to wrap the properties you want to be made unique to each instance.
UNITY_DEFINE_INSTANCED_PROP(float4, color) This defines a per-instance shader property with a type and a name. In this example the color property is unique.
UNITY_SETUP_INSTANCE_ID(v); This makes the instance ID accessible to shader functions. It must be used at the very beginning of a vertex shader, and is optional for fragment shaders.
UNITY_TRANSFER_INSTANCE_ID(v, o); This copies the instance ID from the input structure to the output structure in the vertex shader. This is only necessary if you need to access per-instance data in fragment shader.
UNITY_ACCESS_INSTANCED_PROP(color) This accesses a per-instance shader property. It uses instance ID to index into instance data array.

Note: As long as material properties are instanced, renderers can always be rendered instanced, even if you put different instanced properties into different renderers. Normal “non-instanced” properties cannot be batched, so do not put them in the MaterialPropertyBlock; instead, create different materials for them.

A note regarding UnityObjectToClipPos

UnityObjectToClipPos(v.vertex) is always preferred where mul(UNITY_MATRIX_MVP,v.vertex) would otherwise be used. While you can continue to use UNITY_MATRIX_MVP as normal in instanced shaders, UnityObjectToClipPos is the most efficient way of transforming vertex positions from object space into clip space.

In instanced shaders, UNITY_MATRIX_MVP (among other built-in matrices) is transparently modified to include an extra matrix multiply. Specifically, it is expanded to mul(UNITY_MATRIX_VP, unity_ObjectToWorld). unity_ObjectToWorld is expanded to unity_ObjectToWorldArray[unity_InstanceID]). UnityObjectToClipPos is optimized to perform 2 matrix-vector multiplications simultaneously, and is therefore more efficient than performing the multiplication manually as the shader compiler will not automatically perform this optimization.

Further notes

  • Instanced draw calls appear in the Frame Debugger as Draw Mesh (instanced).
  • When writing or modifying your own shaders, don’t forget to instance shadows, too. For a surface shader, use the addshadow option to force the generation of an instanced shadow pass.
  • You don’t have to define per-instance properties, but setting up an instance ID is mandatory, as world matrices need it to work correctly.
  • When using forward rendering, objects affected by multiple lights can’t be instanced efficiently. Only the base pass can make effective use of instancing, not the add passes.
  • Objects that use lightmaps, or are affected by different light or reflection probes, can’t be instanced.
  • If you have more than 2 passes for multi-pass shaders, only the first passes can be instanced. This is because Unity forces the later passes to be rendered together for each object.
  • You need to tell Unity to always calculate the vertex transformation. To do this, multiply by M first, then by VP (VP * M * v) in add passes. This allows you to avoid conflict in the base/first passes caused by small floating point error. Define UNITY_USE_CONCATENATED_MATRICES before including UnityCG.cginc. You don’t need to do this for surface shaders, as it is automatically generated.
  • D3D constant buffers have a maximum size of 64KB. For OpenGL, it’s usually 16KB. You will reach this limit if you try to define too many per-instance properties. The shaders may fail to compile or, even worse, the shader compiler might crash. To work around this, you have to balance between the size of the batch and the size of per-instance properties. Defining UNITY_MAX_INSTANCE_COUNT with an integer before including any .cginc file allows you to limit the maximum number of instances an instanced draw call can draw. This allows for more properties per instance in the instance constant buffer. You can achieve the same result when using a surface shader with #pragma instancing_options maxcount:number. The default value of this max instance count is 500; for OpenGL, the actual value is one quarter of the value you specify - so 125 by default.
  • All the shader macros used in the above example are defined in UnityInstancing.cginc. You can find this file in [Unity folder]\Editor\Data\CGIncludes.
Graphics Command Buffers
Sparse Textures
Copyright © 2023 Unity Technologies
优美缔软件(上海)有限公司 版权所有
"Unity"、Unity 徽标及其他 Unity 商标是 Unity Technologies 或其附属机构在美国及其他地区的商标或注册商标。其他名称或品牌是其各自所有者的商标。
公安部备案号:
31010902002961