在Unity中如果要保存相机图片,我们最常用的方法是首先把RenderTexture转化成Texture2D,如下:
Texture2D texture2D = null;
texture2D = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
var previous = RenderTexture.active;
RenderTexture.active = renderTexture;
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
RenderTexture.active = previous;
texture2D.Apply();
void Awake()
{
targetRenderTexture = new RenderTexture((int)(normalSize), (int)(normalSize), 0 , RenderTextureFormat.ARGB32);//, RenderTextureFormat.Default
int quality = QualitySettings.GetQualityLevel();
if (quality > 0)
targetRenderTexture.antiAliasing = 4;
else
targetRenderTexture.antiAliasing = 1;
}
RenderTexture创建部分
然后再把图片保存成二进制格式进行保存,如下:
byte[] bytes = txtureSmall.EncodeToPNG();
try
{
string forder = GetFileDirectory(path);
bool exists = System.IO.Directory.Exists(forder);
if (!exists)
System.IO.Directory.CreateDirectory(forder);
System.IO.File.WriteAllBytes(path, data);
}
catch (Exception ee)
{
Debug.Log("文件写入失败:" + path + " Error:" + ee.Message);
}
如果是在保存进度,场景切换等允许卡顿的地方操作是没有问题的。
如果是在不允许有卡顿感的操作中就是大问题了。
经过分析主要来自EncodeToPNG函数,耗费了大量CPU。
那么如果我们让他在别的进程运行,游戏就不会有卡顿感了。
马上进行测试:
new System.Threading.Thread(() =>
{
这里处理上面的逻辑。
}).Start();
发现不能在其他进程里处理RenderTexture,txtureSmall.EncodeToPNG,会报出
…… can only be called from the main thread.
那么怎么做呢,其实Unity提供了其他的方法来做这样事情。
下面做一个记录
int nw = rendertxture.width;
int nh = rendertxture.height;
//filesavepath = filename;
GraphicsFormat format = rendertxture.graphicsFormat;
UnityEngine.Rendering.AsyncGPUReadbackRequest request = UnityEngine.Rendering.AsyncGPUReadback.Request(rendertxture, 0 );
yield return 0;
while (!request.done)
{
yield return new WaitForEndOfFrame();
}
if (request.hasError)
{
Debug.Log("GPU readback error detected.");
yield break;
}
byte[] array = request.GetData<byte>().ToArray();
Task.Run(() =>
{
#if UNITY_EDITOR
Debug.Log("保存进度图:" + filename);
#endif
byte[] datas = ImageConversion.EncodeArrayToPNG(array, format, (uint)nw, (uint)nh);
CFunc.StreamWriter(filename, datas);
});
这样主线程就不会卡顿了。
经过一段时间测试后发现
AsyncGPUReadbackRequest的函数,可能会出现报错 This GfxDevice does not support asynchronous readback
发现报错了图片也能显示出来。匪夷所思
然后通过SystemInfo.supportsAsyncGPUReadback函数来判断发现也是False,说明不支持,不支持为说明图可以显示出来呢。
所以研究了下,直接可以通过拿出RenderText的Texture2D,获取到GetPixeds32,然后再放入多线程处理。代码如下,可能速度稍微慢一些。
Texture2D saveToTexture(RenderTexture renderTexture)
{
Texture2D texture2D = null;
texture2D = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
var previous = RenderTexture.active;
RenderTexture.active = renderTexture;
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
RenderTexture.active = previous;
texture2D.Apply();
return texture2D;
}
Texture2D txture = saveToTexture(targetRenderTexture);
Color32[] array = txture.GetPixels32();
//这里如果renderTexture不是RenderTextureFormat.ARGB32,在IOS中好像是BGRA,会导致获取保存的颜色变色,原因未知.
GameObject.DestroyImmediate(txture);
Task.Run(() =>
{
#if UNITY_EDITOR
Debug.Log("保存进度图:" + filename);
#endif
byte[] bytes = ImageConversion.EncodeArrayToPNG(array, format, (uint)nw, (uint)nh);
CFunc.StreamWriter(filename, bytes);
pone.ok = true;
});
相关技术资料
https://docs.unity3d.com/ScriptReference/ImageConversion.EncodeArrayToPNG.html
https://forum.unity.com/threads/asyncgpureadbackrequest-and-encodetopng.551314/
https://forum.unity.com/threads/using-the-job-system-to-save-images.522837/
Unity中优化图片保存:避免卡顿与异步解决方案
这篇博客探讨了在Unity中如何避免在保存相机图片时导致的游戏卡顿问题。主要焦点在于EncodeToPNG函数消耗大量CPU资源。作者提出使用AsyncGPUReadbackRequest进行异步处理,但遇到不支持的设备问题。为了解决这个问题,作者提供了另一种方法,即先将RenderTexture转换为Texture2D,然后在多线程中处理GetPixels32和编码。通过这种方式,主线程的卡顿得到了缓解。博客还包含了错误处理和兼容性检查的建议。
390

被折叠的 条评论
为什么被折叠?



