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

如何在Jetpack Compose中实现可旋转的3D模型展示?

在Jetpack Compose中实现可旋转的3D模型展示

问题说明

你提供的SceneformModel代码无法直接运行——Google官方已停止维护Sceneform,且没有推出适配Jetpack Compose的官方组件。下面提供两种可行的实现方案,均支持手势旋转模型。

方案一:使用Sceneview Compose(推荐,基于Sceneform衍生维护库)

Sceneview是社区维护的Sceneform替代方案,提供了Jetpack Compose适配组件,内置手势交互支持,上手简单。

步骤1:添加依赖

在Module级别的build.gradle中引入依赖:

dependencies {
    implementation 'io.github.sceneview:sceneview-compose:2.0.0'
    implementation 'io.github.sceneview:sceneview:2.0.0'
}

步骤2:实现可旋转的3D模型组件

import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import io.github.sceneview.compose.Scene
import io.github.sceneview.compose.Model
import io.github.sceneview.math.Quaternion
import io.github.sceneview.math.Vector3
import io.github.sceneview.math.toQuaternion

@Composable
fun Rotatable3DModel(modelPath: String) {
    // 记录模型旋转状态
    val modelRotation = remember { mutableStateOf(Quaternion.identity()) }

    Scene(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                // 监听双指旋转手势更新模型角度
                detectTransformGestures { _, _, rotationAngle, _ ->
                    modelRotation.value = modelRotation.value * rotationAngle.toQuaternion()
                }
            }
    ) {
        Model(
            modelUri = modelPath,
            scale = Vector3(1.0f, 1.0f, 1.0f),
            rotation = modelRotation.value,
            position = Vector3(0.0f, 0.0f, -3.0f) // 调整Z轴位置让模型处于视野中心
        )
    }
}

使用示例

// 加载assets目录下的GLB模型文件
Rotatable3DModel(modelPath = "model.glb")

方案二:使用Filament + AndroidView封装(原生高性能渲染)

Filament是Google推出的轻量高性能3D渲染引擎,可通过AndroidView在Compose中集成,完全自定义交互与渲染逻辑。

步骤1:添加依赖

dependencies {
    implementation 'com.google.android.filament:filament-android:1.38.0'
    implementation 'com.google.android.filament:gltfio-android:1.38.0'
}

步骤2:封装Compose组件

import android.content.Context
import android.view.SurfaceView
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.filament.Engine
import com.google.android.filament.View
import com.google.android.filament.gltfio.FilamentAsset
import com.google.android.filament.gltfio.ResourceLoader
import java.nio.ByteBuffer

@Composable
fun FilamentRotatableModel(modelBuffer: ByteBuffer) {
    val context = LocalContext.current
    val rotationY = remember { mutableStateOf(0f) }

    AndroidView(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectTransformGestures { _, _, _, rotation ->
                    rotationY.value += rotation
                }
            },
        factory = { ctx ->
            SurfaceView(ctx).apply {
                initFilamentRender(this, context, modelBuffer, rotationY)
            }
        }
    )

    DisposableEffect(Unit) {
        onDispose {
            Engine.destroyInstance()
        }
    }
}

// 初始化Filament渲染逻辑
private fun initFilamentRender(
    surfaceView: SurfaceView,
    context: Context,
    modelBuffer: ByteBuffer,
    rotationY: androidx.compose.runtime.State<Float>
) {
    val engine = Engine.create()
    val view = engine.createView()
    val scene = engine.createScene()

    // 加载GLTF模型
    val assetLoader = engine.createAssetLoader()
    val resourceLoader = ResourceLoader(engine)
    val asset: FilamentAsset = assetLoader.createAssetFromBuffer(modelBuffer)
    resourceLoader.loadResources(asset)
    scene.addEntities(asset.root)

    // 设置相机参数
    val camera = engine.createCamera()
    camera.setProjection(45f, surfaceView.width.toFloat() / surfaceView.height, 0.1f, 100f)
    camera.lookAt(0f, 0f, 3f, 0f, 0f, 0f, 0f, 1f, 0f)
    view.camera = camera
    view.scene = scene

    // 启动渲染循环并更新旋转
    surfaceView.setOnFrameAvailableListener {
        asset.root.setRotation(0f, rotationY.value, 0f)
        engine.render(view)
    }
}

使用示例

// 从assets读取模型文件转为ByteBuffer
val modelBuffer = remember {
    LocalContext.current.assets.open("model.glb").use { inputStream ->
        ByteBuffer.allocate(inputStream.available()).apply {
            inputStream.read(array())
            rewind()
        }
    }
}
FilamentRotatableModel(modelBuffer)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:52:44