如何调整Scene Kit相机主点,实现移轴/透视控制镜头模拟效果?
SceneKit实现透视控制(移轴)镜头偏移效果解决方案
核心实现思路
SCNCamera没有提供直接的镜头偏移参数,我们可以通过自定义投影矩阵或者相机平移+视口偏移两种方案实现,两种方案都可以保证被拍摄物体相对相机的位置不变,不会出现位移问题。
方案1:自定义投影矩阵实现(最稳定,推荐)
这个方案直接修改透视投影的平截头体边界,和Unity的lensShift实现原理完全一致,不需要改变相机的位置和朝向。
首先给出投影矩阵计算工具函数:
import SceneKit import simd /// 生成带镜头偏移的透视投影矩阵 /// - Parameters: /// - camera: 目标SCNCamera对象,需要提前配置好FOV、zNear、zFar参数 /// - viewSize: 当前SCNView的视图尺寸 /// - shiftX: X轴镜头偏移,取值范围建议[-0.5, 0.5] /// - shiftY: Y轴镜头偏移,取值范围建议[-0.5, 0.5] /// - Returns: 带偏移的投影矩阵 func shiftedProjectionMatrix(camera: SCNCamera, viewSize: CGSize, shiftX: CGFloat, shiftY: CGFloat) -> SCNMatrix4 { let fovRadian = camera.fieldOfView * .pi / 180 let aspectRatio = viewSize.width / viewSize.height let nearPlane = camera.zNear let farPlane = camera.zFar // 计算基础平截头体边界 let top = nearPlane * tan(fovRadian / 2) let bottom = -top let right = top * aspectRatio let left = -right // 应用偏移量 let shiftedLeft = left - shiftX * top * 2 let shiftedRight = right - shiftX * top * 2 let shiftedTop = top - shiftY * top * 2 let shiftedBottom = bottom - shiftY * top * 2 // 生成自定义透视矩阵 return SCNMatrix4MakeFrustum(Float(shiftedLeft), Float(shiftedRight), Float(shiftedBottom), Float(shiftedTop), Float(nearPlane), Float(farPlane)) }
使用步骤:
- 关闭相机自动投影调整,避免系统覆盖自定义矩阵
let camera = SCNCamera() // 配置基础相机参数 camera.fieldOfView = 60 camera.zNear = 0.1 camera.zFar = 1000 // 关闭自动投影,必须配置否则自定义矩阵会被覆盖 camera.automaticallyAdjustsProjection = false
- 按需更新偏移矩阵,视图尺寸变化时也需要重新调用更新
// 示例:仰视高层建筑时向上偏移0.2,消除下大上小的透视畸变 let shiftX: CGFloat = 0 let shiftY: CGFloat = 0.2 camera.projectionTransform = shiftedProjectionMatrix( camera: camera, viewSize: scnView.bounds.size, shiftX: shiftX, shiftY: shiftY )
方案2:相机平移+视口偏移等效方案
如果你不想修改投影矩阵,可以用这个等效方案:
镜头向上偏移等价于相机向下平移对应距离,再调整视口的Y轴偏移把画面拉回中心,保证被拍摄物体居中
代码示例:
// 等效Y轴偏移0.2的效果 let shiftY: CGFloat = 0.2 // 1. 相机反向平移 cameraNode.position.y -= shiftY * 2 // 2. 调整视口偏移,把目标物体拉回画面中心 camera.viewport = CGRect(x: 0, y: -shiftY, width: 1, height: 1)
注意事项
- 偏移量
shiftX和shiftY的建议取值区间为[-0.5, 0.5],超过1会导致画面完全移出视口 - 如果是正交相机,只需要修改上述矩阵计算逻辑中的FOV部分为正交缩放值即可,逻辑和透视相机一致
- 如果需要做偏移动画,可以对偏移值做属性动画,每次动画帧更新时重新计算投影矩阵即可
内容的提问来源于stack exchange,提问作者Andrew Chinery
相关产品推荐
相关产品推荐

