? ? ?之前的项目因序列帧数量量大,图片大,耗费不少的资源,想搞一版GPU版本的序列帧试试水,看看性能,初见成效,但是弊端也蛮明显,就是不能合并drawcall,不过还能凑合着用
? ? 流程大概就是把图片资源全部压进GPU,用Material传递索引数据_Index,shader根据不同的索引来显示不同的图片,这样就大大的解放了CPU端的计算,也算是一种性能的提升吧。不过缺点也挺明显,就是图片集,每个图片必须是2的幂次方,且长宽必须相等,这点来说,对于没那么严格的UI像素,想必问题不大。先上实例看下效果
?再上代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
//规范命名、添加注释、合理封装、限制访问权限、异常处理
public class SequenceFrame : MonoBehaviour, IPointerClickHandler
{
public enum State
{
idle,
playing,
pause
}
public enum State1
{
once,
loop
}
//播放状态(默认、播放中、暂停)
private State play_state;
private RawImage _showImage;
private bool _isSelect;
private int index;
private float tim;
private float waittim;
private bool isplay;
private int _selectMax;
private int _hightMax;
private Material _curMaterial;
private int _curMax;
public float FrameNumber = 30;
public State1 condition = State1.once;
public bool Play_Awake = false;
/// <summary>
/// 点击后是否产生渐变效果
/// </summary>
public bool ISGradualChange = true;
//回调事件
public UnityEvent onCompleteEvent;
public List<Texture2D> SelectTexture2Ds;
public List<Texture2D> HighLighTexture2Ds;
public Shader Shader;
public event Action<SequenceFrame> ClickEvent;
void Awake()
{
tim = 0;
index = 0;
waittim = 1 / FrameNumber;
play_state = State.idle;
isplay = false;
if (SelectTexture2Ds.Count < 1)
{
Debug.LogError("SelectTexture2Ds数组为0,请给SelectTexture2Ds数组添加元素!!!");
}
if (Play_Awake)
{
Play();
}
_showImage = this.GetComponent<RawImage>();
if (_showImage == null) throw new UnityException("没有找到ugui组件");
_curMaterial = new Material(Shader);
InitData();
SetMat(_selectMax);
_curMaterial.SetInt("_Index", 0);
_curMaterial.SetFloat("_Convert",0);
}
private void SetMat(int depth)
{
_curMax =depth;
}
private void InitData()
{
var texs1 = GlobalSetting.LoadImage("_TexArr", SelectTexture2Ds);
_curMaterial.SetTexture("_TexArr", texs1);
_selectMax = texs1.depth;
var texs2 = GlobalSetting.LoadImage("_HightArr", HighLighTexture2Ds);
_curMaterial.SetTexture("_HightArr", texs2);
_hightMax = texs2.depth;
_showImage.material = _curMaterial;
}
void Update()
{
//测试
if (Input.GetKeyDown(KeyCode.A))
{
Play();
}
if (Input.GetKeyDown(KeyCode.S))
{
Replay();
}
if (Input.GetKeyDown(KeyCode.D))
{
Stop();
}
if (Input.GetKeyDown(KeyCode.P))
{
Pause();
}
UpMove();
_farmeCount++;
if (_farmeCount >= 20)
{
_curMaterial.SetFloat("_Convert", 0);
}
}
private void OnDestroy()
{
Debug.Log("Destroy textureArray");
Destroy(_curMaterial);
//删除图集,如果其他ui也用到,会导致其他UI的图片消失
GlobalSetting.Dispose();
}
private void UpMove()
{
//单播
if (condition == State1.once)
{
if (play_state == State.idle && isplay)
{
play_state = State.playing;
index = 0;
tim = 0;
}
if (play_state == State.pause && isplay)
{
play_state = State.playing;
tim = 0;
}
if (play_state == State.playing && isplay)
{
tim += Time.deltaTime;
if (tim >= waittim)
{
tim = 0;
index++;
if (index >= _curMax)
{
index = 0;
//ShowImage.sprite = _curSelectList[index];
_curMaterial.SetInt("_Index", index);
isplay = false;
play_state = State.idle;
//此处可添加结束回调函数
if (onCompleteEvent != null)
{
onCompleteEvent.Invoke();
return;
}
}
// ShowImage.sprite = _curSelectList[index];
_curMaterial.SetInt("_Index", index);
}
}
}
//循环播放
if (condition == State1.loop)
{
if (play_state == State.idle && isplay)
{
play_state = State.playing;
index = 0;
tim = 0;
}
if (play_state == State.pause && isplay)
{
play_state = State.playing;
tim = 0;
}
if (play_state == State.playing && isplay)
{
tim += Time.deltaTime;
if (tim >= waittim)
{
tim = 0;
index++;
if (index >= _curMax)
{
index = 0;
//此处可添加结束回调函数
}
_curMaterial.SetInt("_Index", index);
}
}
}
}
/// <summary>
/// 播放
/// </summary>
public void Play()
{
isplay = true;
}
/// <summary>
/// 暂停
/// </summary>
public void Pause()
{
isplay = false;
play_state = State.pause;
}
/// <summary>
/// 停止
/// </summary>
public void Stop()
{
isplay = false;
play_state = State.idle;
index = 0;
tim = 0;
if (_curMaterial == null)
{
Debug.LogWarning("Image为空,请赋值");
return;
}
_curMaterial.SetInt("_Index", 0);
}
/// <summary>
/// 重播
/// </summary>
public void Replay()
{
isplay = true;
play_state = State.playing;
index = 0;
tim = 0;
}
/// <summary>
/// 点击后改变图片效果
/// </summary>
/// <param name="isShow"></param>
public void ChangeSperite(bool isShow)
{
if (ISGradualChange) return;
if (isShow)
{
if ( _hightMax> 0)
{
SetMat(_hightMax);
}
_isSelect = true;
}
else
{
SetMat( _selectMax);
_isSelect = false;
}
}
private int _farmeCount = 0;
private void ClickEffect()
{
if (_hightMax > 0)
{
_curMaterial.SetFloat("_Convert", 1);
}
_farmeCount = 0;
}
/// <summary>
/// 是否过了点击的间隔时间,防止密密麻麻点击造成的崩溃
/// </summary>
private bool _isInterval = true;
private Coroutine _coroutineInterval;
private void OnEnable()
{
_isInterval = true;
}
/// <summary>
/// 间隔点允许下一次点击的时间
/// </summary>
public float IntervalTime = 0.5f;
public void OnPointerClick(PointerEventData eventData)
{
if (_isInterval)
{
_isInterval = false;
_coroutineInterval = StartCoroutine(GlobalSetting.WaitTime(IntervalTime, (() =>
{
_isInterval = true;
Debug.LogError("恢复点击");
})));
}
else
{
Debug.LogError("不允许点击");
return ;
}
if (ISGradualChange)
{
ClickEffect();
}
else
{
GradualChange();
}
if (ClickEvent != null)
ClickEvent(this);
}
private void OnDisable()
{
SetMat( _selectMax);
}
private void GradualChange()
{
//ShowImage.DOColor(new Color(168 / 255f, 183 / 255f, 255f / 255f, 0.35f), 0.25f).OnComplete((() =>
//{
// ShowImage.DOColor(new Color(255f / 255f, 255f / 255f, 255f / 255f, 1f), 0.25f).SetDelay(0.15f);
//}));
}
}
再来一个工具类
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
public static class GlobalSetting
{
/// <summary>
/// Texture2DArray 限制,长跟宽必须一致,并且符合2的幂次方
/// </summary>
public static int Size = 512;
public static Dictionary<string,Texture2DArray> Texture2DArrayDic=new Dictionary<string, Texture2DArray>();
public static Texture2DArray LoadImage(string key,List<Texture2D> texs)
{
if (Texture2DArrayDic.ContainsKey(key))
{
return Texture2DArrayDic[key];
}
Texture2DArray texture2DArray = null;
if (texs.Count == 0)
{
string[] files = Directory.GetFiles(Application.streamingAssetsPath + "/Pictures");
texs = new List<Texture2D>();
foreach (string file in files)
{
if (file.Contains(".meta")) continue;
byte[] bytes = File.ReadAllBytes(file);
Texture2D tex = new Texture2D(1024, 1024);
tex.LoadImage(bytes);
tex.Apply();
tex = Resize(tex, Size, Size);
texs.Add(tex);
}
texture2DArray = SetTexToGpu(texs.ToArray());
}
else
{
List<Texture2D> temps = new List<Texture2D>();
foreach (Texture2D texture2D in texs)
{
var tex = Resize(texture2D, Size, Size);
temps.Add(tex);
}
texture2DArray = SetTexToGpu(temps.ToArray());
}
Texture2DArrayDic.Add(key, texture2DArray);
return texture2DArray;
}
/// <summary>
/// 缩略图片
/// </summary>
/// <param name="source"></param>
/// <param name="newWidth"></param>
/// <param name="newHeight"></param>
/// <returns></returns>
public static Texture2D Resize(Texture2D source, int newWidth, int newHeight)
{
source.filterMode = FilterMode.Point;
RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight);
rt.filterMode = FilterMode.Point;
RenderTexture.active = rt;
Graphics.Blit(source, rt);
var nTex = new Texture2D(newWidth, newHeight);
nTex.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0);
nTex.Apply();
RenderTexture.active = null;
Object.Destroy(rt);//及时删掉
return nTex;
}
public static Texture2DArray SetTexToGpu(Texture2D[] texs)
{
if (texs == null || texs.Length == 0)
{
return null;
}
if (SystemInfo.copyTextureSupport == CopyTextureSupport.None ||
!SystemInfo.supports2DArrayTextures)
{
return null;
}
Texture2DArray texArr = new Texture2DArray(texs[0].width, texs[0].width, texs.Length, texs[0].format, false, false);
for (int i = 0; i < texs.Length; i++)
{
//拷贝的贴图必须符合2的幂次方
Graphics.CopyTexture(texs[i], 0, 0, texArr, i, 0);
}
texArr.wrapMode = TextureWrapMode.Clamp;
texArr.filterMode = FilterMode.Trilinear;
for (int i = 0; i < texs.Length; i++)
{
Object.Destroy(texs[i]);
}
Resources.UnloadUnusedAssets();
return texArr;
}
public static IEnumerator WaitTime(float time, Action action)
{
yield return new WaitForSeconds(time);
if (action != null) action();
}
//慎重删除
public static void Dispose()
{
foreach (KeyValuePair<string, Texture2DArray> pair in Texture2DArrayDic)
{
var temp = pair.Value;
Object.Destroy(temp);
}
Texture2DArrayDic.Clear();
}
}
其次就是Shader了
Shader "Unlit/SequenceFrame"
{
Properties
{
_MainTex("MainTex",2D)="white" {}
_Speed("Speed",float)=0
}
SubShader
{
Tags{"LightMode" = "ForwardBase" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
LOD 100
Pass
{
// 关闭深度写入
ZWrite Off
// 开启混合模式,并设置混合因子为SrcAlpha和OneMinusSrcAlpha
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_instancing
UNITY_DECLARE_TEX2DARRAY(_TexArr);
UNITY_DECLARE_TEX2DARRAY(_HightArr);
float _Convert;
sampler2D _MainTex;
float _Speed;
struct appdata
{
float4 vertex : POSITION;
float3 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float3 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(int,_Index)
UNITY_INSTANCING_BUFFER_END(Props)
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
fixed4 texColor = UNITY_SAMPLE_TEX2DARRAY(_TexArr, float3(i.uv.xy, UNITY_ACCESS_INSTANCED_PROP(Props, _Index)));
fixed4 texColorHigh = UNITY_SAMPLE_TEX2DARRAY(_HightArr, float3(i.uv.xy, UNITY_ACCESS_INSTANCED_PROP(Props, _Index)));
fixed4 endCol = lerp(texColor,texColorHigh,_Convert);
return endCol;
}
ENDCG
}
}
Fallback "VertexLit"
}
布局图
?
|