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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 13:24:09