image加载图片 ui unity_运行时生成UI图集

本文介绍了Unity中如何在运行时生成动态图集以优化UI渲染效率,避免内存浪费。内容包括动态图集的制作方法,如贴图打包算法和贴图拷贝策略,并提供了使用RenderTexture和RawImage实现动态图集的示例。
摘要由CSDN通过智能技术生成

1. 简介

我们都知道在UI中使用贴图图集可以有效地减少drawcall提升渲染效率,在Unity中制作图集或者通过第三方工具制作图集已经成为标准开发流程。

但是在某些使用场景中,离线制作的静态图集并不是最优做法,比如我们一个界面上要显示两个人物头像,四个物品图标,以及四个技能图标。按照静态图集的流程,我们会制作人物、物品、技能三个图集,运行时同时将这三个图集加载进内存。如果我们的图标数量较大的话,很显然会造成内存浪费。

这时,动态图集就有明显的优势了,我们如果能在运行时将需要的少数图标打包到一张贴图中,既能满足ui合批的需求,又不会造成内存的浪费。

下面我们来讨论一下动态图集的实现。

2. 动态图集的制作

动态图集的制作,主要涉及到两个方面的知识,一是贴图打包算法(贴图该放到什么位置),二是贴图拷贝方法(怎么将贴图拷贝到图集特定位置)

2.1 贴图打包算法

贴图打包算法是一个NP难的问题[1],在[2]中对解决算法有详细的讨论。对于我们动态图集的打包,一是要求实时,二是贴图数量不大,所以不必在这里对算法优劣过多纠结。

在示例中我们实现的是按行列切割矩形的贪婪打包算法,有兴趣的同学可以直接看代码,在这里不多做讨论。

2.2 贴图拷贝方法

Unity中可以将贴图归为两大类,一类是以CPU端操作为主的Texture2D,第二类是以GPU端操作为主的RenderTexture。两种贴图类型各自都有优缺点。Texture2D的使用范围最广,但是修改起来慢,而且有着诸多限制。RenderTexture操作快,但是不支持贴图压缩。

在这里我们使用RenderTexture来做图集贴图类型。那么现在我们需要实现一种将任意贴图渲染到RenderTexture特定位置的方法。由于几次实验一直没办法理解Graphics.DrawTexture的正确用法,所以这里我只好通过魔改 Graphics.Blit 投影矩阵[3]的方法来实现了。

c#端主要代码:

public static void DrawTexture(Texture source, RenderTexture target, RectInt position)
{
	// 构建变换矩阵
	float l = position.x * 2.0f / target.width - 1;
	float r = (position.x + position.width) * 2.0f / target.width - 1;
	float b = position.y * 2.0f / target.height - 1;
	float t = (position.y + position.height) * 2.0f / target.height - 1;
	var mat = new Matrix4x4();
	mat.m00 = r - l;
	mat.m03 = l;
	mat.m11 = t - b;
	mat.m13 = b;
	mat.m23 = -1;
	mat.m33 = 1;

	// 绘制贴图
	uploadMateral.SetMatrix(Shader.PropertyToID("_ImageMVP"), GL.GetGPUProjectionMatrix(mat, true));
	Graphics.Blit(source, target, uploadMateral);
}

Shader主要代码:

float4x4 _ImageMVP;
v2f_img vert (appdata_img v)
{
    v2f_img o;
    o.pos = mul(_ImageMVP, v.vertex);
    o.uv = v.texcoord;
    return o;
}

sampler2D _MainTex;	
fixed4 frag (v2f_img i) : SV_Target
{
    return tex2D(_MainTex, i.uv);
}

2.3 示例

在下图,我们将6张尺寸不一的贴图动态打成图集:

8c236e6d93ed555803751b71bd6c6e8e.png
RenderTexture图集

3. 动态图集的使用

由于我们使用RenderTexture来储存图集贴图,而Unity的Image控件只支持Texture2D,所以我们只能忍痛放弃Image。

RawImage控件是支持RenderTexture的,在使用时我们需要设置RawImage的uvRect属性,使之与图集子贴图位置对应即可。

var image = GetComponent<RawImage>();
image.texture = textureAtlas.texture;
image.uvRect = new Rect(
        (float)position.x / (float)textureAtlas.texture.width, 
        (float)position.y / (float)textureAtlas.texture.height,
        (float)position.width / (float)textureAtlas.texture.width, 
        (float)position.height / (float)textureAtlas.texture.height);

aeb9ecbc9abee9556d11106789ccaf3d.png
RawImage设置

4. 综合示例

jintiao/RuntimeTextureAtlas​github.com
bce6b13fa36baf6dafc58be03f0b8816.png

引用

[1] Bin Packing Problem

[2] Optimal Rectangle Packing

[3] The Perspective and Orthographic Projection Matrix

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值