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

如何在Kotlin Compose UI的Android应用中导入本地3D模型并分视图展示

在Compose中展示本地3D模型+下方描述的实现方案

核心思路

Compose本身没有原生3D渲染能力,我们需要借助Android的Filament库(原Sceneform)处理3D模型渲染,再通过AndroidView把基于View的3D组件包装成Compose可组合项,最后和描述文本完成组合布局。

步骤1:添加依赖

在模块级build.gradle.kts(或build.gradle)中引入Filament相关依赖,支持glb/gltf等主流3D模型格式:

// Filament核心渲染库
implementation "com.google.android.filament:filament-android:1.32.0"
implementation "com.google.android.filament:filament-utils-android:1.32.0"
// 模型加载工具,支持glb/gltf
implementation "com.google.android.filament:gltfio-android:1.32.0"

步骤2:封装Compose可用的3D视图

用AndroidView包装Filament的SurfaceView,实现本地模型加载和渲染:

@Composable
fun Local3DModelView(modelPath: String, modifier: Modifier = Modifier) {
    AndroidView(
        factory = { context ->
            SurfaceView(context).apply {
                holder.addCallback(object : SurfaceHolder.Callback {
                    private lateinit var engine: Engine
                    private lateinit var scene: Scene
                    private lateinit var view: View
                    private lateinit var camera: Camera
                    private lateinit var modelLoader: AssetLoader
                    private lateinit var resourceLoader: ResourceLoader

                    override fun surfaceCreated(holder: SurfaceHolder) {
                        // 初始化Filament核心组件
                        engine = Engine.create()
                        scene = engine.createScene()
                        view = engine.createView()
                        camera = engine.createCamera()

                        // 设置相机视角,适配3D模型预览
                        camera.setProjection(
                            Camera.Projection.PERSPECTIVE,
                            45.0,
                            width.toFloat() / height.toFloat(),
                            0.1f,
                            100.0f
                        )
                        camera.lookAt(0.0f, 1.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f)
                        view.camera = camera

                        // 加载本地模型文件
                        modelLoader = AssetLoader(engine)
                        resourceLoader = ResourceLoader(engine)
                        val asset = modelLoader.createAssetFromFile(context, modelPath)
                        asset?.let {
                            scene.addEntity(it.root)
                            resourceLoader.loadResources(asset)
                        }

                        // 启动渲染循环
                        val renderer = engine.createRenderer()
                        renderer.setViewport(0, 0, width, height)
                        renderer.setSurface(holder.surface)
                        Looper.myLooper()?.let { looper ->
                            Handler(looper).post(object : Runnable {
                                override fun run() {
                                    if (renderer.beginFrame(holder.surface)) {
                                        renderer.render(view)
                                        renderer.endFrame()
                                    }
                                    Handler(looper).postDelayed(this, 16)
                                }
                            })
                        }
                    }

                    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
                        view.viewport = Viewport(0, 0, width, height)
                        camera.setProjection(
                            Camera.Projection.PERSPECTIVE,
                            45.0,
                            width.toFloat() / height.toFloat(),
                            0.1f,
                            100.0f
                        )
                    }

                    override fun surfaceDestroyed(holder: SurfaceHolder) {
                        // 释放Filament资源,避免内存泄漏
                        modelLoader.destroy()
                        resourceLoader.destroy()
                        engine.destroyScene(scene)
                        engine.destroyCamera(camera)
                        engine.destroyView(view)
                        engine.destroy()
                    }
                })
            }
        },
        modifier = modifier.fillMaxWidth().height(300.dp)
    )
}

步骤3:组合3D视图与描述文本

在主可组合函数中用Column布局把两部分整合:

@Composable
fun ModelDetailScreen(modelPath: String, modelDesc: String) {
    Column(
        modifier = Modifier.fillMaxSize().padding(16.dp),
        verticalArrangement = Arrangement.Top
    ) {
        // 上方3D模型展示区
        Local3DModelView(modelPath = modelPath)
        Spacer(modifier = Modifier.height(16.dp))
        // 下方模型描述区
        Text(
            text = modelDesc,
            fontSize = 16.sp,
            lineHeight = 24.sp,
            color = Color.DarkGray
        )
    }
}

关键注意点

  • 模型路径处理:如果模型放在assets目录下,路径格式为"file:///android_asset/your_model.glb";如果在外部存储,要确保应用有对应读取权限(Android 13+需申请READ_MEDIA_IMAGES等权限)
  • 资源释放:必须在surfaceDestroyed中释放Filament的所有资源,否则会导致内存泄漏
  • 交互扩展:如果需要支持模型旋转、缩放,可以在SurfaceView中添加触摸事件监听,修改相机的lookAt参数实现
  • 性能优化:复杂模型建议提前轻量化处理,避免卡顿

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:06:22