当前位置:   article > 正文

Unity3D研究院之ECS渲染Sprite_batchrenderergroup

batchrenderergroup

先我们先借助Unity的SpriteAtlas构建一个图集,同一个图集只会占用一个DrawCall。

 

接着写一个脚本,把需要参与ECS渲染的Sprite拖上去。

 

渲染的原理就是 m_BatchRendererGroup.AddBatch

ECS负责坐标的修改

1.参与渲染的sprite数量发生变化(增加、删除)需要重新m_BatchRendererGroup.AddBatch

2.参与渲染的sprite数量没有发生变化,只是坐标发生变化,那么在Job里重新计算坐标

3.参与渲染的sprite数量没有发生变化、坐标也没有发生变化,例如:血条 显示图片一部分,只需要重新刷新MaterialPropertyBlock即可。

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

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

using System.Collections.Generic;

using Unity.Burst;

using Unity.Collections;

using Unity.Jobs;

using UnityEngine;

using UnityEngine.Rendering;

using static Unity.Mathematics.math;

 

public class GSprite : MonoBehaviour

{

 

    public struct ECS

    {

 

        public Vector3 position; //显示坐标

        public Vector3 scale;    //显示缩放

        public Vector2 pivot;    //锚点区域

    }

    private BatchRendererGroup m_BatchRendererGroup;

    private Mesh m_Mesh;

    private Material m_Material;

    private int m_BatchIndex = -1;

    private JobHandle m_Hadle;

  

    private NativeList<ECS> m_SpriteData;

    private List<Vector4> m_SpriteDataOffset = new List<Vector4>();

 

    public Sprite sprite1;

    public Sprite sprite2;

    public Sprite sprite3;

    public Shader shader;

 

    void Start()

    {

        m_SpriteData = new NativeList<ECS>(100, Allocator.Persistent);

        m_Mesh = Resources.GetBuiltinResource<Mesh>("Quad.fbx");

        m_Material = new Material(shader) { enableInstancing = true };

        m_Material.mainTexture = sprite1.texture;

 

        //添加图片

        AddSprite(sprite1, Vector3.zero, Vector3.one,0.5f); //显示图片一部分(横向0.5f)

        AddSprite(sprite2, Vector3.zero + new Vector3(1,0,0), Vector3.one * 0.5f, 1f); //显示完整图片(整体缩小0.5)

        AddSprite(sprite3, Vector3.zero + new Vector3(1, 1, 0), new Vector3(10,2,1), 1f); //显示完整图片

 

        Refresh();

    }

 

 

    void AddSprite(Sprite sprite, Vector2 localPosition, Vector2 localScale ,float slider)

    {

        float perunit = sprite.pixelsPerUnit;

        Vector3 scale = new Vector3((sprite.rect.width / perunit) * localScale.x, (sprite.rect.height / perunit) * localScale.y, 1f);

        Vector4 rect = GetSpreiteRect(sprite);

        scale.x *= slider;

        rect.x *= slider;

 

        ECS obj = new ECS();

        obj.position = localPosition;

        obj.pivot = new Vector2(sprite.pivot.x / perunit * localScale.x, sprite.pivot.y / perunit * localScale.y);

        obj.scale = scale;

        m_SpriteData.Add(obj);

        m_SpriteDataOffset.Add(rect);

    }

 

 

    private void Refresh()

    {

 

        //1.参与渲染的sprite数量发生变化(增加、删除)需要重新m_BatchRendererGroup.AddBatch

        RefreshElement();

        //2.参与渲染的sprite数量没有发生变化,只是坐标发生变化,那么在Job里重新计算坐标

        RefreshPosition();

        //3.参与渲染的sprite数量没有发生变化、坐标也没有发生变化,例如:血条 显示图片一部分,只需要重新刷新MaterialPropertyBlock即可。

        //RefreshBlock();

    }

    private void RefreshElement()

    {

        if (m_BatchRendererGroup == null)

        {

            m_BatchRendererGroup = new BatchRendererGroup(OnPerformCulling);

        }

        else

        {

            m_BatchRendererGroup.RemoveBatch(m_BatchIndex);

        }

        MaterialPropertyBlock block = new MaterialPropertyBlock();

        block.SetVectorArray("_Offset", m_SpriteDataOffset);

        m_BatchIndex = m_BatchRendererGroup.AddBatch(

          m_Mesh,

          0,

          m_Material,

          0,

          ShadowCastingMode.Off,

          false,

          false,

          default(Bounds),

          m_SpriteData.Length,

          block,

          null);

    }

 

    void RefreshPosition()

    {

        m_Hadle.Complete();

 

        m_Hadle = new UpdateMatrixJob

        {

            Matrices = m_BatchRendererGroup.GetBatchMatrices(m_BatchIndex),

            objects = m_SpriteData,

        }.Schedule(m_SpriteData.Length, 32);

    }

 

 

    void RefreshBlock()

    {

        MaterialPropertyBlock block = new MaterialPropertyBlock();

        block.SetVectorArray("_Offset", m_SpriteDataOffset);

        m_BatchRendererGroup.SetInstancingData(m_BatchIndex, m_SpriteData.Length, block);

    }

 

    public JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCullingContext cullingContext)

    {

        //sprite不需要处理镜头裁切,所以这里直接完成job

        m_Hadle.Complete();

        return m_Hadle;

    }

 

    [BurstCompile]

    private struct UpdateMatrixJob : IJobParallelFor

    {

        public NativeArray<Matrix4x4> Matrices;

        [ReadOnly] public NativeList<ECS> objects;

        public void Execute(int index)

        {

            //通过锚点计算sprite实际的位置

            ECS go = objects[index];

            var position = go.position;

            float x = position.x + (go.scale.x * 0.5f) - go.pivot.x;

            float y = position.y + (go.scale.y * 0.5f) - go.pivot.y;

 

            Matrices[index] = Matrix4x4.TRS(float3(x, y, position.z),

                Unity.Mathematics.quaternion.identity,

                objects[index].scale);

        }

    }

 

 

    private Vector4 GetSpreiteRect(Sprite sprite)

    {

        var uvs = sprite.uv;

        Vector4 rect = new Vector4();

        rect[0] = uvs[1].x - uvs[0].x;

        rect[1] = uvs[0].y - uvs[2].y;

        rect[2] = uvs[2].x;

        rect[3] = uvs[2].y;

        return rect;

    }

 

    private void OnDestroy()

    {

        if (m_BatchRendererGroup != null)

        {

            m_BatchRendererGroup.Dispose();

            m_BatchRendererGroup = null;

        }

        m_SpriteData.Dispose();

    }

}

渲染区域需要动态的传入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

Shader "ECSSprite"

{

Properties

{

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

        _Offset("_Offset",Vector)=(1,1,0,0)

}

SubShader

{

Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }

        Blend SrcAlpha OneMinusSrcAlpha

        Pass

        {

            CGPROGRAM

            #pragma vertex vert

            #pragma fragment frag

            #pragma multi_compile_instancing

#include "UnityCG.cginc"

            #pragma target 3.0

 

struct appdata

{

float4 vertex : POSITION;

    UNITY_VERTEX_INPUT_INSTANCE_ID

float2 uv : TEXCOORD0;

            };

 

struct v2f

{

float2 uv : TEXCOORD0;

float4 vertex : SV_POSITION;

};

 

sampler2D _MainTex;

float4 _MainTex_ST;

        

            UNITY_INSTANCING_BUFFER_START(Props)

            UNITY_DEFINE_INSTANCED_PROP(fixed4, _Offset) //渲染区域

            UNITY_INSTANCING_BUFFER_END(Props)

v2f vert (appdata v)

{

v2f o;

UNITY_SETUP_INSTANCE_ID(v);

o.vertex = UnityObjectToClipPos(v.vertex);

                fixed4 l =  UNITY_ACCESS_INSTANCED_PROP(Props, _Offset);

                o.uv = v.uv.xy * l.xy + l.zw;

                return o;

}

fixed4 frag (v2f i) : SV_Target

{

// sample the texture

fixed4 col = tex2D(_MainTex, i.uv);

return col;

}

ENDCG

}

}

}

最后就是结果 图片锚点、显示图片一部分、或者完整显示图片,一个drawcall全部渲染完毕。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/86220
推荐阅读
相关标签
  

闽ICP备14008679号