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:
Hay un Standard Surface Shader disponible que soporta el instancing. Agregue uno a su proyecto seleccionando Shader > Standard Surface Shader (Instanced).
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:
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);
}
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
}
}
}
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.
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.