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

如何调整SK3DNode内相机位置,使3D模型居中适配视口

如何调整SK3DNode内相机位置,使3D模型居中适配视口

我完全懂你的需求——就是要让3D模型不管在宽屏还是竖屏的SK3DNode视口里,都能完整、居中地显示出来,对吧?你之前的计算思路是对的,但确实没把视口的宽高比考虑进去,这就会导致在不同比例的视口里,模型要么超出范围要么留太多空白。我来帮你完善这个逻辑。

问题核心

相机的实际视野范围不仅由垂直FOV决定,还会被视口的宽高比影响。比如同样的垂直FOV,宽屏视口的水平视野会更宽。所以我们需要同时计算水平和垂直两个方向上,让模型完整显示所需的相机距离,然后取较大的那个距离,这样就能保证模型在两个维度都不会超出视口。

完整实现代码

我把调整后的逻辑整合到你的SK3DNode扩展里了,替换掉原来的TODO部分:

public extension SK3DNode {
  static func forModel(_ model: SCNNode, size: CGSize) -> SK3DNode {
    let scene = SCNScene()
    let node3D = SK3DNode()
    node3D.scnScene = scene
    node3D.viewportSize = size
    
    scene.rootNode.addChildNode(model)

    let camera = SCNCamera()
    let cameraNode = SCNNode()
    cameraNode.camera = camera
    node3D.pointOfView = cameraNode
    
    // 1. 获取模型的包围盒尺寸与中心(确保基于模型整体大小计算)
    let boundingBox = model.boundingBox
    let modelSize = SCNVector3(
      x: boundingBox.max.x - boundingBox.min.x,
      y: boundingBox.max.y - boundingBox.min.y,
      z: boundingBox.max.z - boundingBox.min.z
    )
    let modelCenter = SCNVector3(
      x: (boundingBox.min.x + boundingBox.max.x) / 2,
      y: (boundingBox.min.y + boundingBox.max.y) / 2,
      z: (boundingBox.min.z + boundingBox.max.z) / 2
    )
    
    // 2. 计算视口的宽高比
    let viewportAspect = size.width / size.height
    
    // 3. 计算相机所需的距离(同时考虑水平和垂直视野)
    let verticalFOV = camera.fieldOfView
    let verticalFOVRad = NumberUtil.degreeToRadian(degree: verticalFOV)
    // 根据视口宽高比推导水平FOV弧度
    let horizontalFOVRad = 2 * atan(tan(verticalFOVRad / 2) * viewportAspect)
    
    // 分别计算垂直、水平方向完整显示模型所需的相机距离
    let requiredVerticalDistance = (modelSize.y / 2) / tan(verticalFOVRad / 2)
    let requiredHorizontalDistance = (modelSize.x / 2) / tan(horizontalFOVRad / 2)
    
    // 取最大距离,确保模型在两个方向都能完整显示
    var cameraDistance = max(requiredVerticalDistance, requiredHorizontalDistance)
    
    // 可选:如果需要容纳模型的深度(Z方向尺寸),可以加上深度的一半
    cameraDistance += modelSize.z / 2
    
    // 4. 设置相机位置与看向约束
    cameraNode.position = SCNVector3(x: modelCenter.x, y: modelCenter.y, z: modelCenter.z + cameraDistance)
    let lookAtConstraint = SCNLookAtConstraint(target: model)
    lookAtConstraint.isGimbalLockEnabled = true // 避免相机旋转问题
    cameraNode.constraints = [lookAtConstraint]
    
    return node3D
  }
}

关键细节说明

  1. 包围盒与模型中心:用model.boundingBox获取模型的完整包围盒,计算出实际尺寸和中心位置,确保相机是对着模型的中心,而不是默认原点。
  2. 视口宽高比适配:通过视口的宽高比推导相机的实际水平FOV,这样计算出来的距离会完全匹配当前视口的比例。
  3. 取最大距离:如果只按垂直方向计算,宽屏视口里模型的左右部分可能会超出;只按水平方向计算,竖屏视口里模型的上下部分可能会被切掉。取最大距离能保证两个方向都覆盖到。
  4. 深度适配(可选):如果你的模型有明显的Z方向厚度,可以把模型深度的一半加到相机距离上,这样模型的前后端都能完整显示在视口里。

这样调整后,不管你的SK3DNode是宽的还是竖的,3D模型都会完美居中适配,不会出现部分被截断或者留白过多的情况。

备注:内容来源于stack exchange,提问作者OMGPOP

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:04:29