SwiftUI中SCNView内旋转3D模型被截断问题及透明背景需求
解决SCNView中3D模型旋转截断与背景透明问题
一、背景透明完整设置
你已经设置了view.backgroundColor = UIColor.clear,但还需要给场景本身也设置透明背景,否则可能仍会显示白色:
view.scene?.background.contents = UIColor.clear
另外在SwiftUI中使用MySceneView时,要确保父视图没有设置不透明背景,避免覆盖透明效果。
二、修复模型旋转截断问题(相机配置)
你添加相机后无效果,是因为SCNView默认会自动生成一个相机,你的自定义相机被覆盖了。按以下步骤配置:
1. 完整相机配置代码
修改makeUIView方法,移除默认相机并调整裁剪参数:
func makeUIView(context: Context) -> UIViewType { let view = SCNView() view.backgroundColor = UIColor.clear view.allowsCameraControl = false view.autoenablesDefaultLighting = true // 加载场景并设置透明背景 guard let scene = SCNScene(named: "CD.dae") else { fatalError("无法加载CD.dae模型") } scene.background.contents = UIColor.clear view.scene = scene // 移除默认相机(避免覆盖自定义相机) scene.rootNode.childNodes.filter { $0.camera != nil }.forEach { $0.removeFromParentNode() } // 创建自定义相机并调整裁剪范围 let cameraNode = SCNNode() let camera = SCNCamera() // 调整裁剪面,防止模型被截断,根据模型大小微调 camera.zNear = 0.1 // 近裁剪面,设小避免近处模型被裁 camera.zFar = 100 // 远裁剪面,覆盖模型最大范围 cameraNode.camera = camera // 相机位置:根据CD模型尺寸调整z值,确保完整显示模型 cameraNode.position = SCNVector3(x: 0, y: 0, z: 5) scene.rootNode.addChildNode(cameraNode) // 处理模型节点 for childNode in scene.rootNode.childNodes where childNode.camera == nil { let uprightRotation = SCNMatrix4MakeRotation(.pi / 2, 1, 0, 0) childNode.transform = SCNMatrix4Mult(childNode.transform, uprightRotation) let counterclockwiseRotation = SCNMatrix4MakeRotation(.pi / 2, 0, 0, 1) childNode.transform = SCNMatrix4Mult(childNode.transform, counterclockwiseRotation) if isSpin { let spin = SCNAction.repeatForever(SCNAction.rotateBy(x: CGFloat.pi * 2, y: 0, z: 0, duration: 10)) childNode.runAction(spin) } } return view }
2. 关键调整说明
- 移除默认相机:SCNView会自动生成默认相机,必须先移除才能让自定义相机生效
- 裁剪面参数:
zNear和zFar决定相机可视范围,模型超出这个范围就会被截断,根据你的CD模型大小微调数值 - 相机位置:如果z值过大,模型会过小;过小则可能超出近裁剪面,需要适配模型实际尺寸
三、额外优化点
- 模型尺寸适配:如果模型本身过大,可添加缩放代码:
childNode.scale = SCNVector3(x: 0.5, y: 0.5, z: 0.5) // 根据实际情况调整比例 - 状态更新处理:当前
updateUIView为空,isSpin状态变化时模型不会响应,补充代码:func updateUIView(_ uiView: UIViewType, context: Context) { guard let scene = uiView.scene else { return } for childNode in scene.rootNode.childNodes where childNode.camera == nil { if isSpin { if !childNode.hasActions { let spin = SCNAction.repeatForever(SCNAction.rotateBy(x: CGFloat.pi * 2, y: 0, z: 0, duration: 10)) childNode.runAction(spin) } } else { childNode.removeAllActions() } } }
内容的提问来源于stack exchange,提问作者saladboi
相关产品推荐
相关产品推荐

