Unity中8th Wall XR:RealityTexture保存至磁盘异常求助
嘿,看起来你的代码不仅有截断(最后一行没写完),还踩了8th Wall XR保存相机画面的几个常见坑——比如YUV纹理不能直接存成PNG,还有纹理可读权限、文件权限这些细节问题。我帮你梳理下问题和修复方案:
问题排查与修复方案
1. 先补全并修正核心代码
你的原始代码没写完,而且8th Wall返回的YUV纹理(GetRealityYTexture()/GetRealityUVTexture())是单通道/双通道的,直接保存会丢失颜色信息,必须先转成RGBA格式。下面是完整可运行的实现:
using UnityEngine; using System.IO; public class FrameRecorder : MonoBehaviour { [SerializeField] private XrController mXr; private DirectoryInfo mTakeDirInfo; private FrameInfo mFrameInfo; void Start() { // 初始化保存目录(用persistentDataPath避免权限问题) mTakeDirInfo = new DirectoryInfo(Path.Combine(Application.persistentDataPath, "8thWall_Frames")); if (!mTakeDirInfo.Exists) mTakeDirInfo.Create(); // 假设你的帧计数类是这样定义的 mFrameInfo = new FrameInfo(); } public void RecordAndSaveFrameTexture() { if (mXr.ShouldUseRealityRGBATexture()) { // RGBA纹理可以直接保存 SaveTextureAsPNG(mXr.GetRealityRGBATexture(), Path.Combine(mTakeDirInfo.FullName, $"frame_{mFrameInfo.mFrameCount}.png")); } else { // YUV纹理必须转成RGBA再保存 Texture2D rgbaTexture = ConvertYUVToRGBA(mXr.GetRealityYTexture(), mXr.GetRealityUVTexture()); SaveTextureAsPNG(rgbaTexture, Path.Combine(mTakeDirInfo.FullName, $"frame_{mFrameInfo.mFrameCount}.png")); Destroy(rgbaTexture); // 清理临时纹理,避免内存泄漏 } mFrameInfo.mFrameCount++; } // YUV转RGBA的核心逻辑(适配8th Wall的YUV格式) private Texture2D ConvertYUVToRGBA(Texture2D yTex, Texture2D uvTex) { Texture2D rgbaTex = new Texture2D(yTex.width, yTex.height, TextureFormat.RGBA32, false); rgbaTex.filterMode = FilterMode.Point; Color[] yPixels = yTex.GetPixels(); Color[] uvPixels = uvTex.GetPixels(); Color[] rgbaPixels = new Color[yPixels.Length]; // 用BT.601标准转换(如果颜色不对可以换成BT.709) for (int i = 0; i < yPixels.Length; i++) { float y = yPixels[i].r; float u = uvPixels[i].r - 0.5f; float v = uvPixels[i].g - 0.5f; float r = y + 1.402f * v; float g = y - 0.344f * u - 0.714f * v; float b = y + 1.772f * u; // 把颜色值 clamp 在0-1之间,避免画面失真 rgbaPixels[i] = new Color(Mathf.Clamp01(r), Mathf.Clamp01(g), Mathf.Clamp01(b), 1f); } rgbaTex.SetPixels(rgbaPixels); rgbaTex.Apply(); return rgbaTex; } private void SaveTextureAsPNG(Texture2D texture, string savePath) { // 关键:8th Wall的纹理默认可能不可读,需要先转成可读纹理 Texture2D readableTexture = MakeTextureReadable(texture); byte[] pngData = readableTexture.EncodeToPNG(); File.WriteAllBytes(savePath, pngData); Debug.Log($"帧已保存到:{savePath}"); Destroy(readableTexture); } // 把不可读纹理转成可读的工具方法 private Texture2D MakeTextureReadable(Texture sourceTex) { RenderTexture tempRT = RenderTexture.GetTemporary(sourceTex.width, sourceTex.height); Graphics.Blit(sourceTex, tempRT); RenderTexture previousRT = RenderTexture.active; RenderTexture.active = tempRT; Texture2D readableTex = new Texture2D(sourceTex.width, sourceTex.height); readableTex.ReadPixels(new Rect(0, 0, tempRT.width, tempRT.height), 0, 0); readableTex.Apply(); RenderTexture.active = previousRT; RenderTexture.ReleaseTemporary(tempRT); return readableTex; } // 假设你的FrameInfo类定义 public class FrameInfo { public int mFrameCount = 0; } }
2. 必须注意的几个细节
- 移动端权限:在Android/iOS上,一定要申请存储读写权限,否则文件保存会失败。可以在
Start()方法里加权限申请逻辑:void Start() { #if UNITY_ANDROID || UNITY_IOS if (!Permission.HasUserPermission(Permission.ExternalStorageWrite)) { Permission.RequestUserPermission(Permission.ExternalStorageWrite); } #endif // 其他初始化代码... } - 调用时机:要在相机帧渲染完成后调用
RecordAndSaveFrameTexture,比如放在LateUpdate()或者8th Wall的OnXrFrame回调里,避免读取到不完整的帧数据。 - 颜色校准:如果转换后的画面颜色偏色,把YUV转RGBA的公式换成BT.709标准(适合高清屏幕),只需要修改三个系数:
float r = y + 1.5748f * v; float g = y - 0.1873f * u - 0.4681f * v; float b = y + 1.8556f * u;
3. 快速排查步骤
- 先看控制台报错:如果出现
Texture not readable,说明你没做可读纹理转换;如果是Permission denied,就是权限没申请。 - 输出保存路径:用
Debug.Log(mTakeDirInfo.FullName)打印路径,手动去文件管理器里检查有没有文件生成。 - 先测试RGBA分支:如果
ShouldUseRealityRGBATexture()为true时能保存成功,说明问题出在YUV转RGBA的逻辑上。
内容的提问来源于stack exchange,提问作者Kino
相关产品推荐
相关产品推荐

