簡體   English   中英

SceneKit:圍繞任意點運行而沒有相機跳躍或將錨點移動到中心?

[英]SceneKit: orbit around arbitrary point without camera jumping or shifting anchor point to center?

目標是圍繞場景中任意但可見的點運行。

當用戶平移時,相機應該圍繞這個錨點移動和旋轉。

如果錨點位於屏幕中間,這將非常有效。

然而,如果錨點偏離中心——比如說,在屏幕的左邊緣——攝像機會在將錨點帶入中心時瞬間跳躍。

下面的代碼修復了跳躍問題,但它使用戶迷失方向,因為錨點緩慢地移動到中心。

相機移動與平移手勢有關。

關於如何解決這個問題的任何想法?

let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didSceneViewPanOneFinger))
panRecognizer.minimumNumberOfTouches = 1
panRecognizer.maximumNumberOfTouches = 1
panRecognizer.delegate = self
sceneView.addGestureRecognizer(panRecognizer)


func didSceneViewPanOneFinger(_ sender: UIPanGestureRecognizer) {
    // Pick any visible point as long as it's not in center
    let anchorPoint = SCNVector(-15, 0, 0) 

    // Orbit camera
    cameraNode.orbitPoint(anchorPoint: anchorPoint, translation: sender.translation(in: sender.view!), state: sender.state)
}


class CameraNode: SCNNode {
    // Vars
    let headNode = SCNNode()
    var curXRadians = Float(0)
    var curYRadians = Float(0)
    var directLastTranslationY = Float(0)
    var reach = Float(10)
    var aimingPoint = SCNVector3()
    var lastAnchor:SCNVector3!


    init(reach: Float) {
        super.init()
        self.reach = reach

        // Call <doInit> only after all properties set
        doInit()
    }


    fileprivate func doInit() {
        // Add head node
        headNode.camera = SCNCamera()
        headNode.camera!.zNear = Double(0.1)
        headNode.position = SCNVector3(x: 0, y: 0, z: 0)
        addChildNode(headNode)

        // Position camera
        position = SCNVector3(x: 0, y: minY, z: reach)
    }       


    func orbitPoint(anchorPoint: SCNVector3, translation: CGPoint, state: UIGestureRecognizerState) {
        // Get pan distance & convert to radians
        var xRadians = GLKMathDegreesToRadians(Float(translation.x))
        var yRadians = GLKMathDegreesToRadians(Float(translation.y))

        // Get x & y radians, adjust values to throttle rotate speed
        xRadians = (xRadians / 3) + curXRadians
        yRadians = (yRadians / 3) + curYRadians

        // Limit yRadians to prevent rotating 360 degrees vertically
        yRadians = max(Float(-M_PI_2), min(Float(M_PI_2), yRadians))

        // Save original position
        if state == .began {
            aimingPoint = lastAnchor ?? anchorPoint
        } else {
            aimingPoint = SCNVector3.lerp(vectorStart: anchorPoint, vectorEnd: aimingPoint, t: 0.99)
        }

        // Rotate around <anchorPoint>
        // * Compute distance to <anchorPoint>, used as radius for spherical movement
        let radius = aimingPoint.distanceTo(position)
        var newPoint = getPointOnSphere(aimingPoint, hAngle: yRadians, vAngle: xRadians, radius: radius)
        if newPoint.y < 0 {
            yRadians = directLastTranslationY

            newPoint = getPointOnSphere(aimingPoint, hAngle: yRadians, vAngle: xRadians, radius: radius)
        }

        // Set rotation values to avoid Gimbal Lock
        headNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: yRadians)
        rotation = SCNVector4(x: 0, y: 1, z: 0, w: xRadians)

        print("cam pos: \(position). anchor point: \(anchorPoint). radius: \(radius).")

        // Save value for next rotation?
        if state == .ended {
            curXRadians = xRadians
            curYRadians = yRadians
            lastAnchor = aimingPoint ?? anchorPoint
        }
        directLastTranslationY = yRadians
    }


    // Your position in 3d is given by two angles (+ radius, which in your case is constant)
    // here, s is the angle around the y-axis, and t is the height angle, measured 'down' from the y-axis.
    func getSphericalCoords(_ s: Float, t: Float, r: Float) -> SCNVector3 {
        return SCNVector3(-(cos(s) * sin(t) * r),
                          sin(s) * r,
                          -(cos(s) * cos(t) * r))
    }


    fileprivate func getPointOnSphere(_ centerPoint: SCNVector3, hAngle: Float, vAngle: Float, radius: Float? = nil) -> SCNVector3 {
        // Update <radius>?
        var radius = radius
        if radius == nil {
            radius = reach
        }

        // Compute point & return result
        let p = centerPoint - getSphericalCoords(hAngle, t: vAngle, r: radius!)
        return p
    }
}

如果您想圍繞一個不是相機視圖中心的點旋轉相機,並保持該點不在視圖的中心,那么最好使用約束的三角形排列。

在相機視圖的中心,靠近旋轉點,固定一個虛擬對象,用作相機“看”約束。 這個虛擬對象固定在旋轉點上,因此相機可以根據需要旋轉。

我希望這張圖比我的話解釋得更好:

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM