Unity3D研究院GPU Instancing测试

GPU instancing 很早就支持手机了(Android只支持Opengl ES 3.0),但是我一直不知道将它应用到哪里,刚好最近在调研这个我对它又重新测试了一下。

如果是不动的物体勾选static静态合并批次(40-50帧率)

Unity3D研究院GPU Instancing测试

自定义Shader中勾选Enable GPU Instancing

Unity3D研究院GPU Instancing测试

帧率竟然还不如静态合批次(帧率 30-40)

Unity3D研究院GPU Instancing测试

自定义Shader

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

Shader "SimplestInstancedShader1"

{

    Properties

    {

        _Color ("Color", Color) = (1, 1, 1, 1)

        _MainTex ("Texture", 2D) = "white" {}

 

    }

 

    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;

                float2 uv : TEXCOORD0;

                UNITY_VERTEX_INPUT_INSTANCE_ID

            };

 

            struct v2f

            {

                float2 uv : TEXCOORD0;

                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)

 

 

            sampler2D _MainTex;

            float4 _MainTex_ST;

 

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

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

 

                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 tex2D (_MainTex, i.uv) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);

            }

            ENDCG

        }

    }

}

所以,静态不动的物体就没必要用上面的方法了,于是我又测试了Graphics.DrawMeshInstanced()方法,终于满意了。(稳定60帧)

Unity3D研究院GPU Instancing测试

Graphics.DrawMeshInstanced()方法不需要游戏对象以及游戏组件的额外开销,在Update()方法中一气呵成,不过它也有限制,最多可以画1023个。
还有个方法是Graphics.DrawMeshInstancedIndirect()它没有画多少的限制,而且更加灵活,我也搞了好一会儿才在游戏中跑起来,后来才知道它不能再手机上用,只有PC上可以.

在shader中接收位置 颜色 矩阵

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

Shader "Instanced/InstancedIndirectSelection"

{

 

Properties

    {

        _MainTex ("Texture", 2D) = "white" {}

    }

 

SubShader

{

Tags{ "RenderType" = "Opaque" }

LOD 100

 

Pass

{

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#pragma target 4.5

 

#include "UnityCG.cginc"

 

StructuredBuffer<float4> positionBuffer;

StructuredBuffer<float4> colorBuffer;

StructuredBuffer<float4x4> matrix4x4Buffer;

 

struct appdata

{

fixed4 color : COLOR;

    float4 vertex : POSITION;

    float4 texcoord : TEXCOORD0;

};

 

 

struct v2f

{

float4 color: COLOR;

float4 vertex : SV_POSITION;

float2 texcoord : TEXCOORD0;

};

 

sampler2D _MainTex;

            float4 _MainTex_ST;

 

v2f vert(appdata v, uint instanceID : SV_InstanceID)

{

float4 data = positionBuffer[instanceID];

float4x4 materix = matrix4x4Buffer[instanceID / matrix4x4Buffer.Length];

float3 worldPosition = data.xyz + mul(materix,v.vertex.xyz * data.w);

v2f o;

o.vertex = mul(UNITY_MATRIX_VP, float4(worldPosition, 1.0f));

o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

o.color = colorBuffer[instanceID];

return o;

}

 

fixed4 frag(v2f i) : SV_Target

{

return tex2D (_MainTex, i.texcoord) * i.color;

 

}

 

ENDCG

}

}

}

在代码中将位置 颜色 矩阵传进去就行了

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

using UnityEngine;

public class InstancedIndirectExample : MonoBehaviour

{

    public int instanceCount = 100000;

    public Mesh instanceMesh;

    public Material instanceMaterial;

    private ComputeBuffer positionBuffer;

    private ComputeBuffer argsBuffer;

private ComputeBuffer colorBuffer;

private ComputeBuffer matrix4x4Buffer;

 

 

    private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };

    void Start()

{

        argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);

        UpdateBuffers();

    }

 

    void Update()

{

      //  instanceMaterial.SetBuffer("positionBuffer", positionBuffer);

        Graphics.DrawMeshInstancedIndirect(instanceMesh, 0, instanceMaterial, new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)), argsBuffer);

    }

    void UpdateBuffers()

{

if ( instanceCount < 1 ) instanceCount = 1;

 

        // Positions & Colors

        if (positionBuffer != null) positionBuffer.Release();

if (colorBuffer != null) colorBuffer.Release();

if (matrix4x4Buffer != null) matrix4x4Buffer.Release();

 

 

        positionBuffer = new ComputeBuffer(instanceCount, 16);

colorBuffer = new ComputeBuffer(instanceCount, 4*4);

matrix4x4Buffer = new ComputeBuffer(instanceCount,16);

 

 

        Vector4[] positions = new Vector4[instanceCount];

Vector4[] colors = new Vector4[instanceCount];

Matrix4x4[] materix4X4 = new Matrix4x4[instanceCount];

 

        for (int i=0; i < instanceCount; i++)

{

positions [i] = new Vector4 (i, 0f, 0f, 1f);

colors[i] = new Vector4(  1f,  1f,  1f, 1f );

materix4X4[i] = Matrix4x4.TRS (positions [i], Quaternion.Euler (0F, 0F, 0F), Vector3.one);

        }

 

        positionBuffer.SetData(positions);

colorBuffer.SetData(colors);

matrix4x4Buffer.SetData (materix4X4);

 

instanceMaterial.SetBuffer("positionBuffer", positionBuffer);

instanceMaterial.SetBuffer("colorBuffer", colorBuffer);

instanceMaterial.SetBuffer("matrix4x4Buffer", matrix4x4Buffer);

 

        // indirect args

        uint numIndices = (instanceMesh != null) ? (uint)instanceMesh.GetIndexCount(0) : 0;

        args[0] = numIndices;

        args[1] = (uint)instanceCount;

        argsBuffer.SetData(args);

    }

 

    void OnDisable()

{

        if (positionBuffer != null) positionBuffer.Release();

        positionBuffer = null;

 

if (colorBuffer != null) colorBuffer.Release();

        colorBuffer = null;

 

        if (argsBuffer != null) argsBuffer.Release();

        argsBuffer = null;

 

if (matrix4x4Buffer != null) matrix4x4Buffer.Release();

matrix4x4Buffer = null;

 

    }

}

如果是发生移动的物体顶点数量在900以内会动态合并批次,如果需要支持更多的顶点GPU Instaning的优势就更明显了。 目前来看草、石头、植被、比较合适用它。