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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:54:17