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
      5
      uint[] 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
    11
    Bounds 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
    121
    Shader "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
    80
    Shader "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/
作者
Kyle Zhou
发布于
2022年6月28日
许可协议