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
相关产品推荐
相关产品推荐

