大家好,

我回答你关于我当前的问题。我已经问了一个关于这个的问题,但没有人能成功帮助我。然后我将解释我的完整问题以及我是如何尝试解决它的。 (我尝试了几件事)

因此,我需要编写一个 lib,它可以添加许多功能,以便管理 3D 世界中的摄像机和对象。为此,我们选择了 SceneKit Framework 来使用 Metal。

我将发布一个非常简化的代码,但所有必要的东西都在这里。

为了说明我的想法,这里是一个 GIF,它解释了我希望我的相机如何表现:** http://i.stack.imgur.com/5tHUl.gif

这来自 Stack 问题(谢谢 rickster):旋转 SCNCamera 节点,查看假想球体周围的对象

目标是加载场景并处理用户平移动作以在 3D 世界中围绕 3D 对象的重心移动相机。这是我的基本简化代码:

import SceneKit
import UIKit

class SceneManager
{
    private let scene: SCNScene
    private let view: SCNView
    private let camera: SCNNode
    private let cameraOrbit: SCNNode

    init(view: SCNView, assetFolder: String, sceneName: String, cameraName: String, backgroundColor: UIColor) {
        self.view = view
        self.scene = SCNScene(named: (assetFolder + "/" + sceneName))!
        if (self.scene.rootNode.childNodeWithName(cameraName, recursively: true) == nil) {
            print("Fatal error: Cannot find camera in scene with name :\"", cameraName, "\"")
            exit(1)
        }
        self.camera = self.scene.rootNode.childNodeWithName(cameraName, recursively: true)! // Retrieve cameraNode created in scene file
        self.camera.removeFromParentNode()
        self.cameraOrbit = SCNNode()
        self.cameraOrbit.addChildNode(self.camera)
        self.scene.rootNode.addChildNode(self.cameraOrbit)
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panHandler(_:)))
        panGesture.maximumNumberOfTouches = 1
        self.view.addGestureRecognizer(panGesture)
        self.view.backgroundColor = backgroundColor
        self.view.pointOfView = cameraNode
        self.view.scene = self.scene
    }

    @objc private func panHandler(sender: UIPanGestureRecognizer) {
        // some code here
    }
}

然后我探索了我在互联网上找到的许多可能的解决方案。我将介绍我探索过的主要解决方案。

1)EulerAngle

Src:旋转 SCNCamera 节点,查看假想球体周围的对象

我想在这个堆栈中应用 rickster 的方法。这是我尝试的代码:

@objc private func panHandler(sender: UIPanGestureRecognizer) {
    if (sender.state == UIGestureRecognizerState.Changed) {
        let scrollWidthRatio = Float(sender.velocityInView(sender.view!).x) / 10000 * -1
        let scrollHeightRatio = Float(sender.velocityInView(sender.view!).y) / 10000
        cameraOrbit.eulerAngles.y += Float(-2 * M_PI) * scrollWidthRatio
        cameraOrbit.eulerAngles.x += Float(-M_PI) * scrollHeightRatio
    }
}

这适用于 Y 轴,但相机在 X 轴上旋转。我不太清楚是什么引用了 eulerAngle,但我注意到 Z 轴永远不会改变。这是旋转不是球形的原因吗?

2)自制解决方案

src:在父节点旋转期间,SceneKit 子节点位置未更改

这是我发布的关于 worldPosition 和 localPosition 的问题。但提出的解决方案不起作用......(或者我不明白)

这是我将主要使用的解决方案。但如果您有其他解决方案,我准备好探索并尝试一下!

理论上,var alpha 是横坐标轴和摄像机位置之间的角度(2D(三角形圆中的 x,z))。我用它来计算在 X 和 Z 旋转轴上应用的比率。目标是进行随后的旋转 2D 计划(x,z)上的一个段。但是这不起作用 self.camera.position 是相对于 cameraOrbit(父节点)协调的,它需要 worldPosition 属性。

相机 worldTransform 的参数是我不理解的矩阵所以我不能使用它。即使我可以使用 worldPosition 属性,我也不确定它是否能很好地工作,因为我的角度和比率应用于 X 轴和 Z 轴。

你怎么看待这个,也许我需要改变我的方法?

@objc private func panHandler(sender: UIPanGestureRecognizer) {
    let cameraROrbitRadius = sqrt(pow(self.camera.position.x, 2) + pow(self.camera.position.y, 2))
    let alpha = cos(self.camera.position.z / self.cameraOrbitRadius) // Get angle of camera
    var ratioX = 1 - ((CGFloat)(alpha) / (CGFloat)(M_PI)) // Get the ratio with angle for apply to Z and X axes rotation
    var ratioZ =  ((CGFloat)(alpha) / (CGFloat)(M_PI))
    // Change direction of rotation depending camera's position in trigonometric circle
    if (self.camera.position.x > 0 && self.camera.position.z < 0) {
        ratioX *= -1
    } else if (self.camera.position.z < 0 && self.camera.position.x < 0) {
        ratioX *= -1
        ratioZ *= -1
    } else if (self.camera.position.z > 0 && self.camera.position.x > 0) {
        ratioZ *= -1
    }
    // Set the angle rotation to add at imaginary sphere (cameraOrbit)
    let xAngleToAdd = (sender.velocityInView(sender.view!).y / 10000) * ratioX
    let yAngleToAdd = (sender.velocityInView(sender.view!).x / 10000) * (-1)
    let zAngleToAdd = (sender.velocityInView(sender.view!).y / 10000) * ratioZ
    let rotation = SCNAction.rotateByX(xAngleToAdd, y: yAngleToAdd, z: zAngleToAdd, duration: 0.5)
    self.cameraOrbit.runAction(rotation)
}

此方法也适用于 Y 轴。但是对象上方的旋转效果很差。我不能简单地解释它,但相机的旋转是这种理论运动的另类。

我想我已经解释了所有重要的事情。如果您有任何想法或提示?

问候,