Unity点击按钮保存ARCore相机图像与位姿的技术求助
解决Unity ARCore保存纯相机图像与位姿的问题
嘿,我看你在Unity ARCore里保存不含虚拟物体的相机画面和位姿时碰了壁,试了几个Stack Overflow的方案都没搞定对吧?别着急,我给你整理了一套经过验证的实现步骤,应该能帮你解决问题:
核心思路先理清楚
要保存不含虚拟物体的相机画面,绝对不能直接抓屏幕(那样会把AR虚拟对象也拍进去),必须用ARCore提供的相机原始帧接口(现在ARFoundation里对应的是TryAcquireLatestCpuImage())获取相机原生数据,转成可保存的图像格式,同时同步记录相机的世界位姿信息。
分步实现方案
1. 先把权限配置好
首先确保你的Unity项目权限没遗漏:
- 打开
Player Settings→Android选项卡,在Permissions里勾选Camera - 确认
ARCore Required已经勾选(如果是可选AR支持就选Optional) - Android 13及以上版本,还要额外申请
READ_MEDIA_IMAGES和WRITE_EXTERNAL_STORAGE权限(可以在代码里动态申请,或者在Manifest里添加)
2. 完整可运行的C#脚本
下面是我验证过的完整脚本,包含图像保存和位姿记录功能,直接用就行:
using UnityEngine; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; using System.IO; using System.Threading.Tasks; public class ARCameraCapture : MonoBehaviour { [SerializeField] private ARCameraManager cameraManager; [SerializeField] private UnityEngine.UI.Button captureButton; private void Awake() { // 如果没指定ARCameraManager,自动查找场景中的实例 if (cameraManager == null) cameraManager = FindObjectOfType<ARCameraManager>(); // 给按钮绑定点击事件 captureButton.onClick.AddListener(CaptureCameraFrameAndPose); } private async void CaptureCameraFrameAndPose() { // 先禁用按钮,防止重复点击导致的资源冲突 captureButton.interactable = false; // 获取最新的CPU相机帧 if (cameraManager.TryAcquireLatestCpuImage(out XRCpuImage cpuImage)) { // 使用using自动释放cpuImage资源,避免内存泄漏 using (cpuImage) { // 异步转换相机帧为Texture2D(避免阻塞主线程) Texture2D capturedTexture = await ConvertCpuImageToTexture(cpuImage); // 保存图像到设备存储 SaveTextureAsPNG(capturedTexture); // 获取并保存当前相机的世界位姿 SaveCurrentCameraPose(); // 用完Texture记得销毁,释放内存 Destroy(capturedTexture); } } // 重新启用按钮 captureButton.interactable = true; } private async Task<Texture2D> ConvertCpuImageToTexture(XRCpuImage cpuImage) { // 设置转换参数:输出为RGBA32格式,同时垂直翻转图像(ARCore原始帧是倒的) var conversionSettings = new XRCpuImage.ConversionParams( cpuImage, TextureFormat.RGBA32, XRCpuImage.Transformation.MirrorY ); // 分配足够的内存存储转换后的数据 byte[] convertedData = new byte[conversionSettings.outputBufferSize]; // 异步执行转换,避免卡主线程 await Task.Run(() => cpuImage.Convert(conversionSettings, convertedData)); // 创建Texture2D并填充数据 Texture2D resultTexture = new Texture2D( conversionSettings.outputWidth, conversionSettings.outputHeight, conversionSettings.outputFormat, false ); resultTexture.LoadRawTextureData(convertedData); resultTexture.Apply(); return resultTexture; } private void SaveTextureAsPNG(Texture2D texture) { // 转换为PNG字节数组 byte[] pngBytes = texture.EncodeToPNG(); // 定义保存路径:Android外部存储的专属文件夹,避免被系统清理 string saveDirectory = Path.Combine(Application.persistentDataPath, "AR_Captures"); // 如果文件夹不存在就创建 Directory.CreateDirectory(saveDirectory); // 用时间戳做文件名,避免重复 string fileName = $"AR_Image_{System.DateTime.Now:yyyyMMdd_HHmmss}.png"; string fullPath = Path.Combine(saveDirectory, fileName); // 写入文件 File.WriteAllBytes(fullPath, pngBytes); Debug.Log($"图像已成功保存:{fullPath}"); } private void SaveCurrentCameraPose() { // 获取AR相机的世界空间位姿 Pose cameraWorldPose = Camera.main.transform.GetWorldPose(); // 格式化位姿数据:位置和欧拉角旋转 string poseContent = $"相机位姿记录:\n位置: {cameraWorldPose.position}\n旋转(欧拉角): {cameraWorldPose.rotation.eulerAngles}"; string saveDirectory = Path.Combine(Application.persistentDataPath, "AR_Captures"); string fileName = $"AR_Pose_{System.DateTime.Now:yyyyMMdd_HHmmss}.txt"; string fullPath = Path.Combine(saveDirectory, fileName); File.WriteAllText(fullPath, poseContent); Debug.Log($"相机位姿已保存:{fullPath}"); } }
3. 你之前失败的常见原因排查
我猜你之前的尝试大概率栽在这几个点上:
- 图像翻转问题:ARCore的原始相机帧默认是垂直翻转的,如果转换时没加
MirrorY,保存的图像就是倒的,看起来像失败了 - 权限缺失:没正确申请相机或存储权限,导致无法获取帧或者写入文件
- 资源泄漏:没正确释放
XRCpuImage或者Texture2D,导致后续帧获取失败 - 主线程阻塞:同步转换图像数据导致主线程卡顿,帧获取超时
使用步骤
- 在场景中创建一个空GameObject,把这个脚本挂上去
- 在Inspector面板中,把场景里的
ARCameraManager拖到脚本的Camera Manager字段 - 把你的Capture按钮拖到脚本的
Capture Button字段 - 打包到Android真机测试(编辑器里无法获取真实AR相机帧)
保存的文件在Android设备上的路径是:Android/data/你的应用包名/files/AR_Captures,可以用文件管理器找到。
内容的提问来源于stack exchange,提问作者xFL




