首页 文章

Scenekit Pan 2D 翻译到正交 3D 仅水平

提问于
浏览
8

我在 3D 编程方面遇到了更多的数学问题,我希望你能帮助我!

我正在尝试使用具有等角度的 Scenekit 创建 3D 游戏。

这段代码创建了我的正交相机:

var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.name = "Camera"
cameraNode.position = SCNVector3Make(-5.0, -5.0, 10.0)
cameraNode.eulerAngles = SCNVector3Make(PI / 3.0, 0.0, -PI / 4.0)
cameraNode.camera?.usesOrthographicProjection = true
cameraNode.camera?.orthographicScale = 7.0
scene.rootNode.addChildNode(cameraNode)

现在我想使用平移手势移动相机,产生滚动感。为了实现这一点,相机不应垂直移动,只能水平移动。移动时,屏幕上的触摸位置和 3D 世界中未投影的位置应保持不变。

我考虑过计算 2D 平移到 3D 差异并忽略垂直分量。这段代码实际上可以工作,几乎可以产生所需的结果,但速度不正确。如果我平移,相机似乎加速并且没有正确反应:

var previousTranslation = CGPointMake(0.0, 0.0)

func pan(gesture: UIPanGestureRecognizer)
{
    let view = self.view as SCNView
    let translation = gesture.translationInView(view)
    let location = gesture.locationInView(view)

    let diffTrans = translation - previousTranslation
    previousTranslation = translation

    let cameraNode = scene.rootNode.childNodeWithName("Camera", recursively: false)

    let worldPointTrans = view.unprojectPoint(SCNVector3Make(-Float(diffTrans.x), -Float(diffTrans.y), 0.0))
    let worldPoint0 = view.unprojectPoint(SCNVector3Make(0.0, 0.0, 0.0))

    var diff = worldPointTrans - worldPoint0
    diff.x = diff.x / Float(cameraNode!.camera!.orthographicScale)
    diff.y = diff.y / Float(cameraNode!.camera!.orthographicScale)
    diff.z = 0
    cameraNode?.position += diff
}

有人知道一种复杂的方法来计算屏幕平移到水平 3D 平移,忽略垂直轴吗?

提前谢谢:)

编辑:潘现在适用于横向翻译。但不是垂直的,因为我将 z 轴上的差异设置为零。

2 回答

  • 9

    我找到了自己的解决方案。

    我正在计算手势(P1-P2)的起始位置处的光线和翻译位置(Q1-Q2)处的光线。现在我有两条光线,让两条光线都与 XY 平面相交,以接收点 P0 和 Q0

    P0 和 Q0 的差异是未投影的翻译。

    这种技术也适用于 non-orthogonal 相机,但我还没有测试过。

    在我看来它是有效的,但如果有人能在数学上证实这个假设,我会很高兴看到:)

    这是代码:

    var previousLocation = SCNVector3(x: 0, y: 0, z: 0)
    
    func pan(gesture: UIPanGestureRecognizer)
    {
        let view = self.view as SCNView
        let translation = gesture.translationInView(view)
    
        let location = gesture.locationInView(view)
        let secLocation = location + translation
    
        let P1 = view.unprojectPoint(SCNVector3(x: Float(location.x), y: Float(location.y), z: 0.0))
        let P2 = view.unprojectPoint(SCNVector3(x: Float(location.x), y: Float(location.y), z: 1.0))
    
        let Q1 = view.unprojectPoint(SCNVector3(x: Float(secLocation.x), y: Float(secLocation.y), z: 0.0))
        let Q2 = view.unprojectPoint(SCNVector3(x: Float(secLocation.x), y: Float(secLocation.y), z: 1.0))
    
        let t1 = -P1.z / (P2.z - P1.z)
        let t2 = -Q1.z / (Q2.z - Q1.z)
    
        let x1 = P1.x + t1 * (P2.x - P1.x)
        let y1 = P1.y + t1 * (P2.y - P1.y)
    
        let P0 = SCNVector3Make(x1, y1,0)
    
        let x2 = Q1.x + t1 * (Q2.x - Q1.x)
        let y2 = Q1.y + t1 * (Q2.y - Q1.y)
    
        let Q0 = SCNVector3Make(x2, y2, 0)
    
        var diffR = Q0 - P0
        diffR *= -1
    
        let cameraNode = view.scene!.rootNode.childNodeWithName("Camera", recursively: false)
    
        switch gesture.state {
        case .Began:
            previousLocation = cameraNode!.position
            break;
        case .Changed:
            cameraNode?.position = previousLocation + diffR
            break;
        default:
            break;
        }
    }
    

    红色是屏幕翻译,蓝色是世界翻译

  • 1

    我已经计算了等距平移的方程式,代码如下。

    //camera pan ISOMETRIC logic
    func pan(gesture: UIPanGestureRecognizer) {
        let view = self.sceneView as SCNView
        let cameraNode = view.scene!.rootNode.childNode(withName: "Camera", recursively: false)
        let translation = gesture.translation(in: view)
    
        let constant: Float = 30.0
        var translateX = Float(translation.y)*sin(.pi/4.0)/cos(.pi/3.0)-Float(translation.x)*cos(.pi/4.0)
        var translateY = Float(translation.y)*cos(.pi/4.0)/cos(.pi/3.0)+Float(translation.x)*sin(.pi/4.0)
        translateX = translateX / constant
        translateY = translateY / constant
    
        switch gesture.state {
        case .began:
            previousLocation = cameraNode!.position
            break;
        case .changed:
            cameraNode?.position = SCNVector3Make((previousLocation.x + translateX), (previousLocation.y + translateY), (previousLocation.z))
            break;
        default:
            break;
        }
    }
    

    要获得正确的缩放,您需要将 screenheight 用作 orthographicScale 的变量。我在这里使用的缩放是 30 倍放大,注意 30 也用于上面代码中的常量。

    let screenSize: CGRect = UIScreen.main.bounds
        let screenHeight = screenSize.height
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.name = "Camera"
        let cameraDist = Float(20.0)
        let cameraPosX = cameraDist*(-1.0)*cos(.pi/4.0)*cos(.pi/6.0)
        let cameraPosY = cameraDist*(-1.0)*sin(.pi/4.0)*cos(.pi/6.0)
        let cameraPosZ = cameraDist*sin(.pi/6)
        cameraNode.position = SCNVector3Make(cameraPosX, cameraPosY, cameraPosZ)
        cameraNode.eulerAngles = SCNVector3Make(.pi / 3.0, 0.0, -.pi / 4.0)
        cameraNode.camera?.usesOrthographicProjection = true
        cameraNode.camera?.orthographicScale = Double(screenHeight)/(2.0*30.0) //30x magnification constant. larger number = larger object
        scene.rootNode.addChildNode(cameraNode)
    

相关问题