ios - 旋转 SCNCamera 节点,观察一个假想球体周围的物体

标签 ios swift scenekit

我在位置 (30,30,30) 有一个 SCNCamera,在位置 (0,0,0) 的对象上有一个 SCNLookAtConstraint。我试图让相机使用 UIPanGestureRecognizer 围绕假想球体上的对象旋转,同时保持相机和对象之间的半径。我假设我应该使用四元数投影,但我在这方面的数学知识很糟糕。我已知的变量是 x 和 y 平移 + 我试图保持的半径。我用 Swift 编写了该项目,但同样可以接受 Objective-C 中的答案(希望使用标准的 Cocoa Touch Framework)。

地点:

private var cubeView : SCNView!;
private var cubeScene : SCNScene!;
private var cameraNode : SCNNode!;

这是我设置场景的代码:

// setup the SCNView
cubeView = SCNView(frame: CGRectMake(0, 0, self.width(), 175));
cubeView.autoenablesDefaultLighting = YES;
self.addSubview(cubeView);

// setup the scene
cubeScene = SCNScene();
cubeView.scene = cubeScene;

// setup the camera
let camera = SCNCamera();
camera.usesOrthographicProjection = YES;
camera.orthographicScale = 9;
camera.zNear = 0;
camera.zFar = 100;

cameraNode = SCNNode();
cameraNode.camera = camera;
cameraNode.position = SCNVector3Make(30, 30, 30)  
cubeScene.rootNode.addChildNode(cameraNode)

// setup a target object
let box = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 0);
let boxNode = SCNNode(geometry: box)
cubeScene.rootNode.addChildNode(boxNode)

// put a constraint on the camera
let targetNode = SCNLookAtConstraint(target: boxNode);
targetNode.gimbalLockEnabled = YES;
cameraNode.constraints = [targetNode];

// add a gesture recogniser
let gesture = UIPanGestureRecognizer(target: self, action: "panDetected:");
cubeView.addGestureRecognizer(gesture);

下面是手势识别器处理的代码:

private var position: CGPoint!;

internal func panDetected(gesture:UIPanGestureRecognizer) {

    switch(gesture.state) {
    case UIGestureRecognizerState.Began:
        position = CGPointZero;
    case UIGestureRecognizerState.Changed:
        let aPosition = gesture.translationInView(cubeView);
        let delta = CGPointMake(aPosition.x-position.x, aPosition.y-position.y);

        // ??? no idea...

        position = aPosition;
    default:
        break
    }
}

谢谢!

最佳答案

将您的问题分解为子问题可能会有所帮助。

设置场景

首先,考虑如何组织场景以实现您想要的 Action 类型。你谈到移动相机,就好像它附在一个看不见的球体上一样。用这个主意!与其试图通过数学运算将 cameraNode.position 设置到假想球体上的某个点,不如想想如果将相机连接到球体,您将如何移动相机。也就是说,只需旋转球体即可。

如果您想将球体与其余场景内容分开旋转,您可以将其附加到一个单独的节点。当然,您实际上不需要插入 sphere geometry进入你的场景。只需创建一个节点,其 position 与您希望相机环绕的对象同心,然后将相机附加到该节点的子节点。然后您可以旋转该节点来移动相机。这是一个快速演示,没有滚动事件处理业务:

let camera = SCNCamera()
camera.usesOrthographicProjection = true
camera.orthographicScale = 9
camera.zNear = 0
camera.zFar = 100
let cameraNode = SCNNode()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 50)
cameraNode.camera = camera
let cameraOrbit = SCNNode()
cameraOrbit.addChildNode(cameraNode)
cubeScene.rootNode.addChildNode(cameraOrbit)

// rotate it (I've left out some animation code here to show just the rotation)
cameraOrbit.eulerAngles.x -= CGFloat(M_PI_4)
cameraOrbit.eulerAngles.y -= CGFloat(M_PI_4*3)

这是您在左侧看到的内容,右侧是其工作原理的可视化。方格球体是 cameraOrbit,绿色圆锥体是 cameraNode

camera rotate around cube camera rotate visualization

这种方法有几个好处:

  • 您不必在笛卡尔坐标系中设置初始相机位置。只需将它放置在您想要的沿 z 轴的任意距离即可。因为 cameraNodecameraOrbit 的子节点,所以它自己的位置保持不变——相机由于 cameraOrbit 的旋转而移动。
  • 只要您只想让相机指向这个假想球体的中心,就不需要注视约束。相机指向它所在空间的-Z方向——如果你在+Z方向移动它,然后旋转父节点,相机将始终指向父节点的中心(即旋转中心) .

处理输入

现在您已经为相机旋转构建了场景,将输入事件转换为旋转非常容易。简单程度取决于您所追求的控制类型:

  • 寻找轨迹球旋转? (它非常适合直接操作,因为您感觉就像在物理上插入 3D 对象上的一个点。)有一些 questions and answers关于这一点已经在 SO 上了——他们中的大多数人使用 GLKQuaternion。 (更新:GLK 类型在 Swift 1.2/Xcode 6.3 中“有点”可用。在这些版本之前,您可以通过桥接头在 ObjC 中进行数学运算。)
  • 对于更简单的替代方案,您可以将手势的 x 轴和 y 轴映射到节点的偏航角和俯仰角。它不像 arcball 旋转那么漂亮,但它很容易实现——您需要做的就是计算出一个点到弧度的转换,该转换涵盖您所追求的旋转量。

无论哪种方式,您都可以跳过一些手势识别器样板,并通过使用 UIScrollView 获得一些方便的交互行为。 (并不是说坚持使用手势识别器没有用处——这只是一个易于实现的替代方案。)

将一个放在 SCNView 的顶部(不要在其中放置另一个 View 以进行滚动)并将其 contentSize 设置为其帧大小的倍数...然后在滚动过程中,您可以将 contentOffset 映射到您的 eulerAngles:

func scrollViewDidScroll(scrollView: UIScrollView) {
    let scrollWidthRatio = Float(scrollView.contentOffset.x / scrollView.frame.size.width)
    let scrollHeightRatio = Float(scrollView.contentOffset.y / scrollView.frame.size.height)
    cameraOrbit.eulerAngles.y = Float(-2 * M_PI) * scrollWidthRatio
    cameraOrbit.eulerAngles.x = Float(-M_PI) * scrollHeightRatio
}

一方面,你必须为infinite scrolling做更多的工作。如果您想在一个或两个方向上无休止地旋转。另一方面,您会得到很好的滚动式惯性和弹跳行为。

关于ios - 旋转 SCNCamera 节点,观察一个假想球体周围的物体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35236445/

相关文章:

ios - InApp购买应用拒绝

ios - 沙箱与 Podfile.lock 不同步

ios - AWS Lambda Swift 3 错误

php - Swift:使用 Alamofire 从 MYSQL 获取 JSON 数组值

ios - SceneKit 节点跟随相机

iphone - 从不同线程访问 UIView 的简单快捷方式

ios - 购买Apple商店产品导致启动时出现 "Purchased"状态

swift - 创建一个带有子模块的 Swift 框架

scenekit - 何时使用 SCNGeometrySource

ios - Swift unity 光线转换等效