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

