赞
踩
先我们先借助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全部渲染完毕。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。