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

ARCore ArSceneView截图异常求助:捕获时场景视图始终透明

ARCore截图踩坑:ARSceneView总是截成透明的?

最近想给我的AR应用加个截图按钮,要捕获带附加3D节点的ARCore相机视图,但试了好几种方法都栽坑里了——其他屏幕内容都能正常截到,唯独ARSceneView总是显示透明。我试过的方法都列在下面了,有没有大佬能帮忙看看?

我尝试过的方法:

方法1:使用View的DrawingCache

ArFragment arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
View view = arFragment.getARSceneView(); //也试过arFragment.getView();

view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);

方法2:自定义View转Bitmap方法

ArFragment arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
View view = arFragment.getARSceneView();
Bitmap bitmap = getBitmapFromView(view);

public Bitmap getBitmapFromView(View view) {
    view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    view.buildDrawingCache();
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable drawable = view.getBackground();
    if (drawable != null) drawable.draw(canvas);
    view.draw(canvas);
    return returnedBitmap;
}

方法3:尝试获取相机Image

Image image = arFragment.getArSceneView().getArFrame().acquireCameraImage();

结果拿到的是完全透明、尺寸还不对的图像,根本没法用。


正确的解决方案:结合相机帧和Scene渲染内容

其实ARSceneView的渲染是走OpenGL的,普通的View截图方法抓不到OpenGL的渲染内容,所以才会显示透明。正确的做法是同时获取相机帧和Scene的渲染结果,把它们合成到一起:

步骤1:获取相机帧并转成Bitmap

首先从ArFrame拿到相机的Image,然后把YUV格式的相机帧转成RGB Bitmap:

private Bitmap convertYuvImageToBitmap(Image image) {
    Image.Plane[] planes = image.getPlanes();
    ByteBuffer yBuffer = planes[0].getBuffer();
    ByteBuffer uBuffer = planes[1].getBuffer();
    ByteBuffer vBuffer = planes[2].getBuffer();

    int ySize = yBuffer.remaining();
    int uSize = uBuffer.remaining();
    int vSize = vBuffer.remaining();

    byte[] nv21 = new byte[ySize + uSize + vSize];
    yBuffer.get(nv21, 0, ySize);
    vBuffer.get(nv21, ySize, vSize);
    uBuffer.get(nv21, ySize + vSize, uSize);

    YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21, image.getWidth(), image.getHeight(), null);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    yuvImage.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 100, out);
    byte[] imageBytes = out.toByteArray();
    return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
}

步骤2:渲染Scene内容到Bitmap

让ARSceneView把包含3D节点的Scene内容渲染到离屏Bitmap:

private Bitmap captureSceneViewBitmap(ARSceneView sceneView) {
    int width = sceneView.getWidth();
    int height = sceneView.getHeight();
    Bitmap sceneBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(sceneBitmap);
    sceneView.draw(canvas);
    return sceneBitmap;
}

步骤3:合成两张Bitmap

把相机帧和Scene内容叠加,Scene的透明区域会自动透出相机画面:

private Bitmap combineBitmaps(Bitmap cameraBitmap, Bitmap sceneBitmap) {
    Bitmap combinedBitmap = Bitmap.createBitmap(cameraBitmap.getWidth(), cameraBitmap.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(combinedBitmap);
    // 先画相机帧作为底图
    canvas.drawBitmap(cameraBitmap, 0, 0, null);
    // 再画Scene内容(透明区域透出相机帧)
    canvas.drawBitmap(sceneBitmap, 0, 0, null);
    return combinedBitmap;
}

完整调用示例

// 点击截图按钮时触发
public void takeScreenshot(View view) {
    ArFragment arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
    ARSceneView sceneView = arFragment.getArSceneView();
    ArFrame frame = sceneView.getArFrame();
    
    if (frame != null) {
        try (Image cameraImage = frame.acquireCameraImage()) {
            // 转相机帧为Bitmap并调整方向
            Bitmap cameraBitmap = convertYuvImageToBitmap(cameraImage);
            Matrix matrix = new Matrix();
            matrix.postRotate(90); // 根据你的设备实际方向调整角度
            cameraBitmap = Bitmap.createBitmap(cameraBitmap, 0, 0, cameraBitmap.getWidth(), cameraBitmap.getHeight(), matrix, true);
            
            // 捕获Scene内容
            Bitmap sceneBitmap = captureSceneViewBitmap(sceneView);
            // 合成最终截图
            Bitmap finalBitmap = combineBitmaps(cameraBitmap, sceneBitmap);
            
            // 保存到相册
            saveBitmapToGallery(finalBitmap);
            
            // 回收Bitmap避免内存泄漏
            cameraBitmap.recycle();
            sceneBitmap.recycle();
            finalBitmap.recycle();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

private void saveBitmapToGallery(Bitmap bitmap) {
    String fileName = "AR_Screenshot_" + System.currentTimeMillis() + ".png";
    FileOutputStream out = null;
    try {
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), fileName);
        out = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
        // 通知图库更新
        MediaScannerConnection.scanFile(this, new String[]{file.getAbsolutePath()}, null, null);
        Toast.makeText(this, "截图已保存到相册", Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "截图保存失败", Toast.LENGTH_SHORT).show();
    } finally {
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

关键注意点:

  • 务必调整相机Bitmap的旋转角度,相机获取的原始帧方向通常和屏幕显示方向不一致,根据你的设备实际情况修改旋转值。
  • 使用try-with-resources管理Image对象,避免资源泄漏。
  • 及时回收Bitmap,防止内存溢出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:29:34