https://docs.unity3d.com/2019.4/Documentation/Manual/GPUInstancing.html
use GPU instancing to draw (or render) multiple copies of the same mesh at once, using a small number of draw calls.
it is useful for drawing objects such as buildings, trees and grass, or other things that appear repeatedly in a Scene.
GPU instancing only renders identical meshes with each draw call, but each instance can have different parameters (for example, color or scale) to add variation and reduce the appearance of repetition.
GPU instancing can reduce the number of draw calls used per scene. this significantly improves the rendering performance of your project.
adding instancing to your materials
to enbale GPU instancing on materials, select your material in the project window, and in the inspector, tick the enable instancing checkbox.
unity only displays this checkbox if the Material Shader supports GPU Instancing.
when u use GPU instancing, the following restrictions apply:
- unity automatically picks MeshRenderer components and Graphics.DrawMesh calls for instancing. note that SkinnedMeshRenderer is not supported.
- unity only batches GameObjects that share the same Mesh and the same Material in a single GPU instancing draw call. use a small number of Meshes and Materials for better instancing efficiency. to create variations, modify your shader scripts to add per-instance data.
u can also use the calls Graphics.DrawMeshInstanced and Graphics.DrawMeshInstancedIndirect to perform GPU instancing from your scripts.
adding per-instance data
by default, unity only batches instances of GameObjects with different Transforms (position, rotation, scale) in each instanced draw call.
to add more variance to your instanced GameObjects, modify your shader to add per-instance properties such as Material color.
the example below demonstrates how to create an instanced shader with different colour values for each instance.
Shader "Custom/InstancedColorSurfaceShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use Shader model 3.0 target
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
when u declare _Color as an instanced property, unity will gather _Color values from the MaterialPropertyBlock objects set on GameObjects and put them in a single draw call.
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);
}
note that in normal cases (where an instancing shader is not used, or _Color is not a per-instance property), draw call batches are broken due to different values in the MaterialPropertyBlock.
For these changes to take effect, you must enable GPU Instancing. To do this, select your Shader in the Project window, and in the Inspector, tick the Enable Instancing checkbox.
adding instancing to vertex and fragment shaders
the following example takes a simple unlit shader and makes it capable of instancing with different colors:
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
}
}
}
UnityInstancing.cginc有宏定义。
shader modifications
-
#pragma multi_compile_instancing
use this to instruct unity to generate instancing variants. it is not necessary for surface shaders. -
UNITY_VERTEX_INPUT_INSTANCE_ID
use this in the vertex Shader input/output structure to define an instance ID. see SV_InstanceID for more information. -
UNITY_INSTANCING_BUFFER_START(name) / UNITY_INSTANCING_BUFFER_END(name)
every per-instance property must be defined in a specially named constant buffer. use this pair of macros to wrap the properties u want to be made unique to each instance. -
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
use this to define a per-instance shader property with a type and a name. in this example, the _Color property is unique. -
UNITY_SETUP_INSTANCE_ID(v);
use this to make the instance ID accessible to shader functions. it must be used at the very begining of a vertex shader, and is optional for fragment shaders. -
UNITY_TRANSFER_INSTANCE_ID(v, o);
use this to copy the instance ID from input structure to the output structure in the vertex shader.
this is only necessary if u need to access per-instance data in the fragment shader. -
UNITY_ACCESS_INSTANCED_PROP(arrayName, color)
use this to access a per-instance shader property declared in an instancing constant buffer. it uses an instance ID to index
into the instance data array. the arrayName in the macro must match the one in UNITY_INSTANCING_BUFFER_END(name) macro.