SceneKit中SCNNode远离屏幕中心时出现扭曲变形问题
问题现象
构建SceneKit俯视视角场景,相机位于SCNVector3(0.0, 0.0, 100.0),拍摄平面上滚动的单个球体:
- 球体初始处于屏幕中心时显示完全正常:

- 球体距离屏幕中心越远,扭曲拉伸效果越明显,靠近屏幕边缘时会被拉伸为蛋形:

核心复现代码如下:
球体节点配置:
let ballNode = SCNNode() let sphereGeometry = SCNSphere(radius: MainData.screenWidth*0.005) let targetMaterial = SCNMaterial() targetMaterial.diffuse.contents = UIColor.red sphereGeometry.materials = [targetMaterial] sphereGeometry.firstMaterial?.diffuse.contents = targetMaterial ballNode.geometry = sphereGeometry ballNode.geometry?.firstMaterial?.lightingModel = .phong ballNode.position = SCNVector3(0.0,0.0,20.0) ballNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.dynamic, shape: SCNPhysicsShape.init(node: ballNode)) ballNode.physicsBody?.categoryBitMask = CollisionTypes.dynamicObjects ballNode.physicsBody?.collisionBitMask = CollisionTypes.staticObjects ballNode.physicsBody?.contactTestBitMask = CollisionTypes.nothing ballNode.physicsBody?.isAffectedByGravity = true ballNode.physicsBody?.allowsResting = false scnView.scene?.rootNode.addChildNode(ballNode)
承载平面(实际为SCNBox节点)配置:
let platform = SCNNode() let platformGeometry = SCNBox(width: MainData.screenWidth, height: MainData.screenHeight, length: 2.0, chamferRadius: 0.0) platform.geometry = platformGeometry platform.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.static, shape: SCNPhysicsShape.init(node: platform)) platform.physicsBody?.categoryBitMask = CollisionTypes.staticObjects platform.physicsBody?.collisionBitMask = CollisionTypes.dynamicObjects platform.physicsBody?.contactTestBitMask = CollisionTypes.nothing platform.physicsBody?.isAffectedByGravity = false platform.physicsBody?.allowsResting = true scnView.scene?.rootNode.addChildNode(platform)
问题原因
该变形是透视投影的广角畸变效果,属于3D渲染的正常光学现象,和球体、平面的节点配置、物理模拟都没有关系:
- SceneKit的
SCNCamera默认使用透视投影,默认视场角约60度,等效于日常使用的广角镜头。当相机距离渲染平面较近、需要覆盖大尺寸可视范围时,视场边缘的物体会出现明显的径向拉伸,和现实中广角镜头拍摄画面边缘物体变形的原理完全一致。 - 当前配置下,相机与球体的z轴间距仅为80,承载平面尺寸为全屏宽高,球体移动到平面边缘时,和相机视轴的夹角已经很大,透视投影的拉伸效果就会把正球形拉成蛋形;球体处于屏幕中心时刚好落在视轴上,因此没有畸变,显示正常。
解决方法
根据俯视滚球的场景需求,二选一即可:
- 方案1:改用正交投影(最适配俯视角类游戏需求)
正交投影没有近大远小和边缘畸变,所有位置的物体渲染比例完全一致,只需修改相机配置:guard let cameraNode = scnView.pointOfView else { return } // 开启正交投影 cameraNode.camera?.usesOrthographicProjection = true // 调整正交缩放值适配平面尺寸,可根据实际显示效果微调 cameraNode.camera?.orthographicScale = Double(MainData.screenWidth) / 6.0 // 保持原有相机位置即可 cameraNode.position = SCNVector3(0, 0, 100) - 方案2:保留透视投影,模拟长焦镜头效果
如果需要保留透视的近大远小效果,可以缩小相机视场角同时拉远相机位置,大幅降低边缘畸变:guard let cameraNode = scnView.pointOfView else { return } // 缩小视场角到30度以内,畸变会基本不可见 cameraNode.camera?.fieldOfView = 30 // 拉远相机保证可视范围依然覆盖整个平面,z值可根据实际效果微调 cameraNode.position = SCNVector3(0, 0, 250)
内容的提问来源于stack exchange,提问作者West1
相关产品推荐
相关产品推荐

