非AR场景SceneView自定义旋转后3D模型偏移无法居中问题
问题描述
通过SceneView实现3D模型加载视图时,添加自定义模型旋转控制类后,模型出现向上的位置偏移,无法在屏幕居中显示,偏移效果如下:
需要在保留现有旋转交互功能的前提下,修复模型居中显示问题。
原实现代码如下:
class DragRotationController(private val transformableNode: DragTransformableNode, gestureRecognizer: DragGestureRecognizer ) : BaseTransformationController<DragGesture>(transformableNode, gestureRecognizer) { companion object { private const val initialLat = 26.15444376319647 private const val initialLong = 18.995950736105442 var lat: Double = initialLat var long: Double = initialLong } private var rotationRateDegrees = 0.5f public override fun canStartTransformation(gesture: DragGesture): Boolean { return transformableNode.isSelected } private fun getX(lat: Double, long: Double): Float { return (transformableNode.radius * cos(Math.toRadians(lat)) * sin(Math.toRadians(long)) ).toFloat() } private fun getY(lat: Double): Float { return transformableNode.radius * sin(Math.toRadians(lat)).toFloat() } private fun getZ(lat: Double, long: Double): Float { return (transformableNode.radius * cos(Math.toRadians(lat)) * cos(Math.toRadians(long)) ).toFloat() } override fun onActivated(node: Node?) { super.onActivated(node) Handler(Looper.getMainLooper()).postDelayed({ transformCamera(lat, long) }, 0) } public override fun onContinueTransformation(gesture: DragGesture) { val rotationAmountY = gesture.delta.y * rotationRateDegrees val rotationAmountX = gesture.delta.x * rotationRateDegrees val deltaAngleY = rotationAmountY.toDouble() val deltaAngleX = rotationAmountX.toDouble() long -= deltaAngleX lat += deltaAngleY transformCamera(lat, long) } private fun transformCamera(latitude: Double, longitude: Double) { transformableNode.scene?.camera?.apply { localRotation = getRotation(longitude, latitude) localPosition = getPosition(latitude, longitude) } } private fun getPosition(latitude: Double, longitude: Double) = Vector3( getX(latitude, longitude), getY(latitude), getZ(latitude, longitude) ) private fun getRotation(longitude: Double, latitude: Double): Quaternion? { var rotation = Quaternion.eulerAngles(Vector3(0F, 0F, 0F)) rotation = Quaternion.multiply( rotation, Quaternion( Vector3(0f, 1f, 0f), (longitude).toFloat() ) ) rotation = Quaternion.multiply( rotation, Quaternion( Vector3.right(), (-latitude).toFloat() ) ) return rotation } public override fun onEndTransformation(gesture: DragGesture) { //no-op } }
问题原因
偏移核心来自两点:
- 初始经纬度参数设置不合理,初始纬度26度对应的相机Y轴位置本身偏高,视线没有对准模型中心原点
- 手动计算相机四元数旋转存在逻辑误差,没有做对准模型中心的校正,直接导致视角上模型整体上移
修复方案
调整初始参数,替换手动旋转计算逻辑为SceneView内置的中心对准方法,完全保留原有拖拽旋转交互,修改后代码如下:
class DragRotationController(private val transformableNode: DragTransformableNode, gestureRecognizer: DragGestureRecognizer ) : BaseTransformationController<DragGesture>(transformableNode, gestureRecognizer) { companion object { // 重置初始经纬度,初始状态下相机正对模型中心 private const val initialLat = 0.0 private const val initialLong = 0.0 var lat: Double = initialLat var long: Double = initialLong } private var rotationRateDegrees = 0.5f public override fun canStartTransformation(gesture: DragGesture): Boolean { return transformableNode.isSelected } private fun getX(lat: Double, long: Double): Float { return (transformableNode.radius * cos(Math.toRadians(lat)) * sin(Math.toRadians(long)) ).toFloat() } private fun getY(lat: Double): Float { return transformableNode.radius * sin(Math.toRadians(lat)).toFloat() } private fun getZ(lat: Double, long: Double): Float { return (transformableNode.radius * cos(Math.toRadians(lat)) * cos(Math.toRadians(long)) ).toFloat() } override fun onActivated(node: Node?) { super.onActivated(node) Handler(Looper.getMainLooper()).postDelayed({ transformCamera(lat, long) }, 0) } public override fun onContinueTransformation(gesture: DragGesture) { val rotationAmountY = gesture.delta.y * rotationRateDegrees val rotationAmountX = gesture.delta.x * rotationRateDegrees val deltaAngleY = rotationAmountY.toDouble() val deltaAngleX = rotationAmountX.toDouble() // 增加纬度边界限制,避免旋转到模型上下方出现视角翻转 lat = (lat + deltaAngleY).coerceIn(-89.0, 89.0) long -= deltaAngleX transformCamera(lat, long) } private fun transformCamera(latitude: Double, longitude: Double) { transformableNode.scene?.camera?.apply { localPosition = getPosition(latitude, longitude) // 直接对准模型中心位置,保证模型始终处于视野正中 lookAt(transformableNode.worldPosition) } } private fun getPosition(latitude: Double, longitude: Double) = Vector3( getX(latitude, longitude), getY(latitude), getZ(latitude, longitude) ) public override fun onEndTransformation(gesture: DragGesture) { //no-op } }
修改说明
- 初始经纬度重置为0,初始状态下相机在Z轴正方向正对模型中心,消除初始偏移
- 删除误差较大的手动四元数旋转计算逻辑,使用内置
lookAt()方法直接对准模型节点中心,从根源避免视角偏移 - 增加纬度范围限制,避免拖拽旋转到模型正上方/正下方时出现视角翻转问题
- 原有拖拽旋转的交互逻辑、旋转速率参数完全保留,不影响原有使用体验
内容的提问来源于stack exchange,提问作者Ludiras
相关产品推荐
相关产品推荐

