如何使用SceneForm从ViewRenderable/Node生成对应bitmap?
可实现的三种方案(按匹配度从高到低排序)
方案1:Filament离屏渲染(100%匹配AR场景显示效果)
该方案基于SceneForm底层的Filament渲染引擎实现,可完整保留ViewRenderable在AR场景中的透视变形、透明通道、光照、旋转缩放效果,和实际显示效果完全一致:
- 从
ArSceneView实例中获取Filament核心实例:val engine = arSceneView.engine val renderer = arSceneView.renderer val currentCamera = arSceneView.scene.camera - 创建对应分辨率的离屏渲染纹理,开启RGBA通道保留透明效果:
val outputWidth = 1080 // 可自定义输出宽度 val outputHeight = 1920 // 可自定义输出高度 val offscreenTexture = Texture.Builder() .width(outputWidth) .height(outputHeight) .format(Texture.InternalFormat.RGBA8) .usage(Texture.Usage.COLOR_ATTACHMENT or Texture.Usage.COPY_SRC) .build(engine) val renderTarget = RenderTarget.Builder() .attachTexture(RenderTarget.AttachmentPoint.COLOR, offscreenTexture) .build(engine) - 对齐渲染参数和当前AR相机参数,渲染ViewRenderable到离屏纹理:
renderer.renderStandalone( renderTarget, listOf(yourViewRenderableNode), // 仅传入需要截图的节点 currentCamera.viewMatrix, currentCamera.projectionMatrix, null, null ) - 读取纹理像素转成Bitmap:
val buffer = ByteBuffer.allocateDirect(outputWidth * outputHeight * 4) engine.readTexture( offscreenTexture, 0, 0, 0, outputWidth, outputHeight, 0, Texture.Format.RGBA, Texture.Type.UBYTE, buffer ) buffer.rewind() val resultBitmap = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888) resultBitmap.copyPixelsFromBuffer(buffer) - 用完后销毁纹理和渲染目标避免内存泄漏:
engine.destroy(renderTarget) engine.destroy(offscreenTexture)
方案2:全屏截图裁剪(实现成本最低)
如果目标ViewRenderable没有被其他3D节点遮挡,可通过全屏截图裁剪实现,代码量最小:
- 调用SceneForm封装的截图方法拿到全屏AR帧:
arSceneView.captureSingleFrame { fullScreenBitmap -> // 计算目标节点的屏幕边界矩形 val nodePosition = yourViewRenderableNode.worldPosition val screenPoint = arSceneView.worldToScreenPoint(nodePosition) val nodeWidth = (yourViewRenderable.sizer as ViewSizer).size.x val nodeHeight = (yourViewRenderable.sizer as ViewSizer).size.y // 换算边界坐标后裁剪 val left = (screenPoint.x - nodeWidth/2).coerceAtLeast(0f).toInt() val top = (screenPoint.y - nodeHeight/2).coerceAtLeast(0f).toInt() val width = nodeWidth.toInt().coerceAtMost(fullScreenBitmap.width - left) val height = nodeHeight.toInt().coerceAtMost(fullScreenBitmap.height - top) val resultBitmap = Bitmap.createBitmap(fullScreenBitmap, left, top, width, height) fullScreenBitmap.recycle() }
方案3:原始View直接截图(无3D效果,仅适用于平面无变形场景)
如果你的ViewRenderable不需要保留AR场景中的3D透视、旋转效果,可直接截取传入ViewRenderable的原始View:
val targetView = yourViewRenderable.view targetView.measure( View.MeasureSpec.makeMeasureSpec(targetView.layoutParams.width, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(targetView.layoutParams.height, View.MeasureSpec.EXACTLY) ) targetView.layout(0, 0, targetView.measuredWidth, targetView.measuredHeight) val resultBitmap = Bitmap.createBitmap(targetView.measuredWidth, targetView.measuredHeight, Bitmap.Config.ARGB_8888) val canvas = Canvas(resultBitmap) targetView.draw(canvas)
内容的提问来源于stack exchange,提问作者Monster Brain
相关产品推荐
相关产品推荐

