Version: 5.4
Buffers de comando de gráficos (buffers de comando de gráficos)
Sparse Textures (Texturas Dispersas)

GPU Instancing

Introducción

Usted puede utilizar GPU instancing para dibujar muchos objetos idénticos con solo un poco de draw calls (llamadas de dibujo). Hay algunas restricciones que necesita tener en cuenta:

  • Sus objetos idénticos necesitan compartir el mismo mesh y el mismo material. Sin embargo, puede agregar datos por instancia; Consulte Agregando dados por-instancia, abajo para obtener más información.
  • El componente MeshRenderer y la Graphics.DrawMesh API se soportan.
  • GPU instancing está disponible en las siguientes plataformas:
    • Windows: DX11 / DX12 con SM 4.0 y posterior; OpenGL 4.1 y posterior
    • OS X & Linux: OpenGL 4.1 y posterior
    • PlayStation 4
    • Xbox One

Agregando el instancing a sus objetos

Hay un Standard Surface Shader disponible que soporta el instancing. Agregue uno a su proyecto seleccionando Shader > Standard Surface Shader (Instanced).

Agregando el Standard Instanced Shader
Agregando el Standard Instanced Shader

Aplique este shader al material de su GameObject. En su ventana Inspector del Materia, oprima el desplegable Shader, pasee sobre el campo Instanced y escoja su instanced shader de la lista:

Asignando el Standard Instanced Shader a un Material
Asignando el Standard Instanced Shader a un Material

Agregando datos por-instancia

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

Agregando instancing a sus propios shaders

Tomemos un unlit shader simple y hagamos que sea capaz de 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
        }
    }
}

Código agregado

Addition: Función:
#pragma multi_compile_instancing multi_compile_instancing genera un shader con dos variants: una con una palabra clave incorporada INSTANCING_ON definida (permitiendo instanciar), la otra sin nada definido. Esto permite que el shader retroceda a una versión no instanciada si la instancia no es compatible con la GPU.
UNITY_INSTANCE_ID Esto se utiliza en la estructura de entrada/salida del vertex shader para definir un ID de instancia. Consulte SV_InstanceID para obtener más información.
UNITY_INSTANCING_CBUFFER_START(name) / UNITY_INSTANCING_CBUFFER_END Cada propiedad por instancia debe definirse en un búfer constante nombrado especialmente. Utilice este par de macros para ajustar las propiedades que desea que sean exclusivas de cada instancia.
UNITY_DEFINE_INSTANCED_PROP(float4, color) Esto define una propiedad shader por-instancia con un tipo y un nombre. En este ejemplo, la propiedad color es única.
UNITY_SETUP_INSTANCE_ID(v); Esto hace que el ID de instancia sea accesible por funciones shader. Se debe utilizar al principio de un vertex shader, y es opcional para fragment shaders.
UNITY_TRANSFER_INSTANCE_ID(v, o); Esto copia el ID de instancia de la estructura de entrada a la estructura de salida en el vertex shader. Esto sólo es necesario si necesita acceder a los datos por instancia en el fragment shader.
UNITY_ACCESS_INSTANCED_PROP(color) Esto accede a una propiedad shader por-instancia. Utiliza el ID de instancia para indexar al arreglo de datos de instancias.

Nota: Siempre que las propiedades del material se instancien, los renderizadores siempre se pueden renderizar instanciados, incluso si se ponen diferentes propiedades instanciadas en renderizadores diferentes. Las propiedades normales “no instanciadas” no pueden ser agrupadas por lotes, así que no las coloque en el MaterialPropertyBlock; En su lugar, crear diferentes materiales para ellos.

Una nota sobre UnityObjectToClipPos

UnityObjectToClipPos(v.vertex) siempre se prefiere donde mul(UNITY_MATRIX_MVP,v.vertex) se usaría de otra manera. Mientras que puede continuar usando UNITY_MATRIX_MVP como normal en los shaders instanciados, UnityObjectToClipPos es la forma más eficiente de transformar posiciones de vértice desde el espacio del objeto al espacio del clip.

En shaders instanciados, UNITY_MATRIX_MVP (entre otras matrices incorporadas) se modifica de forma transparente para incluir una multiplicación de matriz adicional. Específicamente, se amplía a mul(UNITY_MATRIX_VP, unity_ObjectToWorld) unity_ObjectToWorld se expande a unity_ObjectToWorldArray[unity_InstanceID]). UnityObjectToClipPos está optimizado para realizar 2 multiplicaciones de vector-matriz simultáneamente y, por lo tanto, es más eficiente que realizar la multiplicación manualmente, ya que el compilador del shader no realizará automáticamente esta optimización.

Notas adicionales

  • Los draw calles de instancias aparecen en el Frame Debugger como Draw Mesh (instanced).
  • Al escribir o modificar sus propios shaders, no olvide instanciar sombras también. Para un surface shader, utilice la opción addshadow para obligar la generación de un pass de shader instanciado.
  • No es necesario definir propiedades por instancia, pero configurar un ID de instancia es obligatorio, ya que las matrices global lo necesitan para que funcionen correctamente.
  • Cuando utilice forward rendering, los objetos afectados por varias luces no se pueden instanciar eficientemente. Solamente el pass base puede hacer uso efectivo del instancing, no el pass para agregar.
  • Los objetos que utilizan lightmaps, o están afectados por una luz diferentes o reflection probes, no se pueden instanciar.
  • Si tiene más de 2 pass para shaders multi-pass, sólo se pueden instanciar los primeros pass. Esto se debe a que Unity obliga a los pass posteriores a ser renderizados juntos para cada objeto.
  • Necesita decirle a Unity que siempre calcule la transformación del vértice. Para hacer esto, multiplique por M primero, luego por VP (VP * M * v) en passes de agregar. Esto le permite evitar conflictos en los passes base/primeros causados por pequeños errores de punto flotante. Defina UNITY_USE_CONCATENATED_MATRICES antes de incluir UnityCG.cginc. No es necesario hacer esto para los surface shaders, ya que se genera automáticamente.
  • Los búferes constantes D3D tienen un tamaño máximo de 64KB. Para OpenGL, usualmente es de 16KB. Usted alcanzará este límite si intenta definir demasiadas propiedades por instancia. Los shaders pueden fallar al compilar o, peor aún, el compilador shader podría bloquearse. Para evitar esto, tiene que equilibrar entre el tamaño del lote y el tamaño de las propiedades por instancia. Definir UNITY_MAX_INSTANCE_COUNT con un entero antes de incluir cualquier archivo .cginc le permite limitar el número máximo de instancias que puede dibujar un draw call instanced. Esto permite más propiedades por instancia en el buffer constante de instancia. Puede obtener el mismo resultado al usar un surface shader con #pragma instancing_options maxcount:number. El valor predeterminado de este recuento de instancias máximo es 500; Para OpenGL, el valor real es una cuarta parte del valor especificado - por lo que 125 es por defecto.
  • 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.
Buffers de comando de gráficos (buffers de comando de gráficos)
Sparse Textures (Texturas Dispersas)