Unity Shader GPU Instancing
GPU Instancing
- DrawMeshInstanced // 提供Matrix[]数组,最多绘制1023个对象
- DrawMeshInstancedIndirect // 通过ComputeBuffer提供数据、数量
- bufferWithArgs = int[5]
- index count per instance // 每个实例index数量
- instance count // 实例数量
- start index location // index开始位置
- base vertex location // vertex开始位置
- start instance location // 实例开始位置
1
2
3
4
5uint[] args = new uint[5] {0, 0, 0, 0, 0};
args[0] = (uint) instanceMesh.GetIndexCount(subMeshIndex);
args[1] = (uint) instanceCount;
args[2] = (uint) instanceMesh.GetIndexStart(subMeshIndex);
args[3] = (uint) instanceMesh.GetBaseVertex(subMeshIndex);
- MaterialPropertyBlock // 用于存储buffer数据
- https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedIndirect.html
- https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedProcedural.html
- DrawMeshInstancedProcedural
1
2
3
4
5
6
7
8
9
10
11Bounds DrawBounds = new Bounds();
DrawBounds.size = Vector3.one * 100000;
DrawBounds.center = MainCamera.transform.position;
// StructuredBuffer<float4x4> IndirectShaderDataBuffer;
ComputeBuffer instanceOutputBuffer = new ComputeBuffer(InstanceCount, sizeof(float) * 16);
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
mpb.SetBuffer("IndirectShaderDataBuffer", instanceOutputBuffer);
Graphics.DrawMeshInstancedProcedural(mesh, 0, material, DrawBounds, InstanceCount, mpb); - Surface Shader设置
- pragma multi_compile_instancing // Shader开启instancing
- pragma instancing_options procedural:setup // 定义setup函数对输入buffer数据处理,对应unity_InstanceID
代码
- vert/frag版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121Shader "Test/GPUInstance"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader
{
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
#pragma target 4.5
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
#include "AutoLight.cginc"
sampler2D _MainTex;
#if SHADER_TARGET >= 45
StructuredBuffer<float4x4> IndirectShaderDataBuffer;
#endif
struct v2f
{
float4 pos : SV_POSITION;
float2 uv_MainTex : TEXCOORD0;
float3 ambient : TEXCOORD1;
float3 diffuse : TEXCOORD2;
float3 color : TEXCOORD3;
SHADOW_COORDS(4)
};
inline float4x4 InverseMatrix(float4x4 input)
{
#define minor(a,b,c) determinant(float3x3(input.a, input.b, input.c))
float4x4 cofactors = float4x4(
minor(_22_23_24, _32_33_34, _42_43_44),
-minor(_21_23_24, _31_33_34, _41_43_44),
minor(_21_22_24, _31_32_34, _41_42_44),
-minor(_21_22_23, _31_32_33, _41_42_43),
-minor(_12_13_14, _32_33_34, _42_43_44),
minor(_11_13_14, _31_33_34, _41_43_44),
-minor(_11_12_14, _31_32_34, _41_42_44),
minor(_11_12_13, _31_32_33, _41_42_43),
minor(_12_13_14, _22_23_24, _42_43_44),
-minor(_11_13_14, _21_23_24, _41_43_44),
minor(_11_12_14, _21_22_24, _41_42_44),
-minor(_11_12_13, _21_22_23, _41_42_43),
-minor(_12_13_14, _22_23_24, _32_33_34),
minor(_11_13_14, _21_23_24, _31_33_34),
-minor(_11_12_14, _21_22_24, _31_32_34),
minor(_11_12_13, _21_22_23, _31_32_33)
);
#undef minor
return transpose(cofactors) / determinant(input);
}
inline float3 ObjectToWorldNormal(float3 norm, float4x4 ObjectToWorld)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
return normalize(mul((float3x3)ObjectToWorld, norm));
#else
return normalize(mul(norm, (float3x3)InverseMatrix(ObjectToWorld)));
#endif
}
v2f vert(appdata_full v, uint instanceID : SV_InstanceID)
{
v2f o;
float4 worldPosition;
float3 worldNormal;
#if SHADER_TARGET >= 45
const float4x4 bufferMatrix = IndirectShaderDataBuffer[instanceID];
worldPosition = mul(bufferMatrix, v.vertex);
worldNormal = ObjectToWorldNormal(v.normal, bufferMatrix);
#else
worldPosition = v.vertex;
worldNormal = v.normal;
#endif
float3 ndotl = saturate(dot(worldNormal, _WorldSpaceLightPos0.xyz));
float3 ambient = ShadeSH9(float4(worldNormal, 1.0f));
float3 diffuse = (ndotl * _LightColor0.rgb);
float3 color = v.color;
o.pos = mul(UNITY_MATRIX_VP, float4(worldPosition.xyz, 1));
o.uv_MainTex = v.texcoord;
o.ambient = ambient;
o.diffuse = diffuse;
o.color = color;
TRANSFER_SHADOW(o)
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed shadow = SHADOW_ATTENUATION(i);
fixed4 albedo = tex2D(_MainTex, i.uv_MainTex);
float3 lighting = i.diffuse * shadow + i.ambient;
fixed4 output = fixed4(albedo.rgb * i.color * lighting, albedo.w);
return output;
}
ENDCG
}
}
FallBack "Diffuse"
} - Surface版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80Shader "Test/GPUInstanceSurface"
{
Properties
{
_Color("Base Color", Color) = (1,1,1,1)
_Smoothness("Smoothness", Range(0, 1)) = 0
_Metallic("Metallic", Range(0, 1)) = 0
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
CGPROGRAM
#pragma surface surf Standard addshadow nolightmap
#pragma multi_compile_instancing
#pragma instancing_options procedural:setup
half4 _Color;
half _Smoothness;
half _Metallic;
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
StructuredBuffer<float4x4> IndirectShaderDataBuffer;
#endif
float4x4 inverse(float4x4 input)
{
#define minor(a,b,c) determinant(float3x3(input.a, input.b, input.c))
float4x4 cofactors = float4x4(
minor(_22_23_24, _32_33_34, _42_43_44),
-minor(_21_23_24, _31_33_34, _41_43_44),
minor(_21_22_24, _31_32_34, _41_42_44),
-minor(_21_22_23, _31_32_33, _41_42_43),
-minor(_12_13_14, _32_33_34, _42_43_44),
minor(_11_13_14, _31_33_34, _41_43_44),
-minor(_11_12_14, _31_32_34, _41_42_44),
minor(_11_12_13, _31_32_33, _41_42_43),
minor(_12_13_14, _22_23_24, _42_43_44),
-minor(_11_13_14, _21_23_24, _41_43_44),
minor(_11_12_14, _21_22_24, _41_42_44),
-minor(_11_12_13, _21_22_23, _41_42_43),
-minor(_12_13_14, _22_23_24, _32_33_34),
minor(_11_13_14, _21_23_24, _31_33_34),
-minor(_11_12_14, _21_22_24, _31_32_34),
minor(_11_12_13, _21_22_23, _31_32_33)
);
#undef minor
return transpose(cofactors) / determinant(input);
}
struct Input
{
half param : COLOR;
};
void setup()
{
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
unity_ObjectToWorld = IndirectShaderDataBuffer[unity_InstanceID];
unity_WorldToObject = inverse(unity_ObjectToWorld);
#endif
}
void surf(Input input, inout SurfaceOutputStandard o)
{
o.Albedo = _Color.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Smoothness;
}
ENDCG
}
FallBack "Diffuse"
}
参考
Unity Shader GPU Instancing
https://automask.github.io/wild/2022/06/28/lab/S_Unity_Shader_Instancing/