You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 快速排查步骤

  1. 先看控制台报错:如果出现Texture not readable,说明你没做可读纹理转换;如果是Permission denied,就是权限没申请。
  2. 输出保存路径:用Debug.Log(mTakeDirInfo.FullName)打印路径,手动去文件管理器里检查有没有文件生成。
  3. 先测试RGBA分支:如果ShouldUseRealityRGBATexture()为true时能保存成功,说明问题出在YUV转RGBA的逻辑上。

内容的提问来源于stack exchange,提问作者Kino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:10:49