Texture2DArray的细节与坑

由于Texture2DArray只能通过代码来创建,由于不同平台Texture格式不同,且没有编辑器的支持,导致需要人为离线制作多套不同格式的资源。而在转换格式时遇到了不少问题,本文统一整理下。

创建Texture2DArray的步骤

  • 首先new一个Texture2DArray对象
1
Texture2DArray texArr = new Texture2DArray(width, height, number, format, false, false);
  • 然后通过Graphics.CopyTexture 或 Texutre2DArray.SetPixels 来将Texture加入到Texture2DArray里面
1
2
3
4
Graphics.CopyTexture(textures[i], 0, texArr, i);
//或
texArr.SetPixels(textures[i].GetPixels(), i);
texArr.Apply(); //如果是SetPixels才需要Apply()
  • 最后通过AssetDatabase.CreateAsset将Texture2DArray序列化存储到.asset后缀的文件中
1
AssetDatabase.CreateAsset(texArr, "Assets/texArray.asset");

API细节

Texture2DArray构造函数

1
public **Texture2DArray**(int **width**, int **height**, int **depth**, TextureFormat **textureFormat**, bool **mipChain**, bool **linear** = true);
  • Texture2DArray 和 Texture2D 在编辑器支持方面的区别

    • Texture2D

    • Texture2DArray

    • 从Inspector面板上可以看到,Unity编辑器在对Texture2DArray方面的支持非常少,只有Warp、Filter及Aniso三个选项。也就意味着,Texture2DArray在创建之后,除了这三个选项外,其他内容都无法修改(至少基于我目前的研究是无能为力的)

    • 所以Texture2DArray构造函数的每个参数都非常重要

  • 由Texture2DArray构造函数的参数来看其缺陷

    • Width和Height,限定了Texture2DArray里面所有的Texture2D的尺寸都必须一致,跟传统的图集不同,传统的UI图集支持每个UI不同的尺寸。

    • Depth,意味着初始化的时候,就必须知道该Texture2DArray将存储多少张Texture2D,多一张或少一张,都必须得重新创建Texture2DArray

    • TextureFormat,在创建Texture2DArray时就必须指定格式,编辑器不支持自动转换格式,这是最大的坑。假设Texture2DArray要参与进工具中进行一些读写操作,那可能要求需要无压缩RGBA32格式;移动平台上,为了节省内存的占用,需要牺牲纹理质量来对纹理进行压缩,会采用各种Compressed格式等;在编辑器不支持的情况下,转换格式只能重新创建Texture2DArray了。

    • mipChain,也即是mipChain,一般需要根据实际情况来开关,而Texture2DArray也只能在创建前就得想好是否用的上,比如一般BlendMap就不需要打开。

    • Linear,线性色彩空间,也需要根据实际情况来打开,比如Normal Map就需要打开Linear。而对于Texutre2D资源,当我们选择TextureType为NormalMap时()就以及帮我们转换为Linear Color Space了。关于色彩空间的知识详见《重新认识Gamma与Linear色彩空间

  • 重新创建Texture2DArray会导致些什么问题?

    • 每个资源都有各自的guid,存储在meta文件中,而组件与组件间的引用,组件与资源间的引用,资源与资源间的引用,互相存储的都是guid,假设重新创建Texture2DArray,那么最大的问题将会是由guid变更引发的资源引用丢失。

      资源的GUID

    • 为了转换格式,不得不创新创建Texture2DArray。首先Unity提供的Texture2DArray类的接口是真的少,没有转换格式的接口,没有压缩纹理的接口。假设针对移动端需要压缩纹理,其中一个做法既是将Texture2DArray中每一张Texture2D都单独拿出来,通过Texture2D的Compress接口对纹理进行压缩,然后再重新创建Texture2DArray。尺寸为512*512共16张纹理的Texture2DArray,进行一次压缩实测也都需要几秒钟,那么跨平台开发,且资源量大,花费在压缩Texture2DArray的时间是可观的,而且都是有损压缩,压缩后解压缩回不到原本质量。

Graphics.CopyTextureTexutre2DArray.SetPixels

直接引用参考文献[Texture2DArray 功能测试]的结论:

1
2
3
4
5
6
7
// 结论 //
// Graphics.CopyTexture耗时(单位:Tick): 5914, 8092, 6807, 5706, 5993, 5865, 6104, 5780 //
// Texture2DArray.SetPixels耗时(单位:Tick): 253608, 255041, 225135, 256947, 260036, 295523, 250641, 266044 //
// Graphics.CopyTexture 明显快于 Texture2DArray.SetPixels 方法 //
// Texture2DArray.SetPixels 方法的耗时大约是 Graphics.CopyTexture 的50倍左右 //
// Texture2DArray.SetPixels 耗时的原因是需要把像素数据从cpu传到gpu, 原文: Call Apply to actually upload the changed pixels to the graphics card //
// 而Graphics.CopyTexture只在gpu端进行操作, 原文: operates on GPU-side data exclusively //
  • Graphics.CopyTexture和Texture2DArray.SetPixels,这两种方法都可以把图像信息传给Texture2DArray对象的每一个子Texture2D。整体上Graphics.CopyTexture 方法要比 Texture2DArray.SetPixels 快的多,大概是40到50倍的样子,原因大概是 Graphics.CopyTexture 只在GPU端进行操作,而Texture2DArray.SetPixels在CPU端操作,操作结束后需要调用Apply方法把图片数据传给GPU,所以比较耗时,Texture2DArray.Apply文档 中也建议如果不需要在CPU上读取像素信息的话建议使用更快的 Graphics.CopyTexture 方法。

Texture2DArray.Apply

Actually apply all previous SetPixels changes.

Apply is a potentially expensive operation, so you’ll want to change as many pixels as possible between Apply calls.

Alternatively, if you don’t need to access the pixels on the CPU, you could use Graphics.CopyTexture for fast GPU-side texture data copies. Note that calling Apply may undo the results of previous calls to Graphics.CopyTexture.

划重点:

  • 如果需要在CPU中读取像素信息,则需要使用Texture2DArray.SetPixels,然后Apply上步Set Pixels的操作。

  • 调用Apply()可能会撤销Graphics.CopyTexture的结果,所以用Graphics.CopyTexture不能也不需要Apply()

  • Apply()是一个昂贵的操作,所以尽量在处理完所有像素后再Apply()

参考

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×