旋转SCNCamera节点,查看假想球体周围的对象 [英] Rotate SCNCamera node looking at an object around an imaginary sphere

查看:498
本文介绍了旋转SCNCamera节点,查看假想球体周围的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



其中:

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

这是我设置场景的代码:

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

//设置场景
cubeScene = SCNScene();
cubeView.scene = cubeScene;

//设置相机
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)

//设置目标对象
let box = SCNBox (宽度:10,高度:10,长度:10,chamferRadius:0);
let boxNode = SCNNode(geometry:box)
cubeScene.rootNode.addChildNode(boxNode)

//在相机上设置约束
let targetNode = SCNLookAtConstraint( target:boxNode);
targetNode.gimbalLockEnabled = YES;
cameraNode.constraints = [targetNode];

//添加手势识别器
let gesture = UIPanGestureRecognizer(target:self,action:panDetected:);
cubeView.addGestureRecognizer(手势);

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

  private var position:CGPoint!; 
internal func panDetected(gesture:UIPanGestureRecognizer){
switch(gesture.state){
case UIGestureRecognizerState.Began:
position = CGPointZero;
case UIGestureRecognizerState.Changed:
让aPosition = gesture.translationInView(cubeView);
let delta = CGPointMake(aPosition.x-position.x,aPosition.y-position.y);

// ???不知道......

position = aPosition;
默认值:
中断
}
}

谢谢!

解决方案

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



设置场景



首先,考虑如何组织场景以启用所需的运动类型。你谈到移动相机就好像它附着在一个看不见的球体上一样。用这个想法!而不是试图计算出将 cameraNode.position 设置到假想球体上的某个点,而只考虑如果连接相机会移动相机到一个球体。也就是说,只需旋转球体。



如果要将球体与场景内容的其余部分分开旋转,则需要将其附加到单独的节点。当然,您实际上不需要插入关于已经在SO上的答案 - 大多数人使用 GLKQuaternion 。 ( UPDATE: GLK类型在Swift 1.2 / Xcode 6.3中是sorta。在这些版本之前,您可以通过桥接头在ObjC中进行数学运算。)

  • 对于更简单的替代方案,您可以将手势的x和y轴映射到节点的偏航角和俯仰角。它不像弧形旋转一样漂亮,但它很容易实现 - 你需要做的就是计算一个点到弧度的转换,它涵盖了你所追求的旋转量。



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



    SCNView (不在其中放置另一个视图进行滚动)并将其 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
    }

    一方面,你需要为无限滚动如果你想在一个或两个方向无休止地旋转。另一方面,你得到了很好的滚动式惯性和反弹行为。


    I've got an SCNCamera at position(30,30,30) with a SCNLookAtConstraint on an object located at position(0,0,0). I'm trying to get the camera to rotate around the object on an imaginary sphere using A UIPanGestureRecognizer, while maintaining the radius between the camera and the object. I'm assuming I should use Quaternion projections but my math knowledge in this area is abysmal. My known variables are x & y translation + the radius I am trying to keep. I've written the project in Swift but an answer in Objective-C would be equally accepted (Hopefully using a standard Cocoa Touch Framework).

    Where:

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

    Here's my code for setting the scene:

        // 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);
    

    And here is the code for the gesture recogniser handling:

    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
        }
    }
    

    Thanks!

    解决方案

    It might help to break down your issue into subproblems.

    Setting the Scene

    First, think about how to organize your scene to enable the kind of motion you want. You talk about moving the camera as if it's attached to an invisible sphere. Use that idea! Instead of trying to work out the math to set your cameraNode.position to some point on an imaginary sphere, just think about what you would do to move the camera if it were attached to a sphere. That is, just rotate the sphere.

    If you wanted to rotate a sphere separately from the rest of your scene contents, you'd attach it to a separate node. Of course, you don't actually need to insert a sphere geometry into your scene. Just make a node whose position is concentric with the object you want your camera to orbit around, then attach the camera to a child node of that node. Then you can rotate that node to move the camera. Here's a quick demo of that, absent the scroll-event handling business:

    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)
    

    Here's what you see on the left, and a visualization of how it works on the right. The checkered sphere is cameraOrbit, and the green cone is cameraNode.

    There's a couple of bonuses to this approach:

    • You don't have to set the initial camera position in Cartesian coordinates. Just place it at whatever distance you want along the z-axis. Since cameraNode is a child node of cameraOrbit, its own position stays constant -- the camera moves due to the rotation of cameraOrbit.
    • As long as you just want the camera pointed at the center of this imaginary sphere, you don't need a look-at constraint. The camera points in the -Z direction of the space it's in -- if you move it in the +Z direction, then rotate the parent node, the camera will always point at the center of the parent node (i.e. the center of rotation).

    Handling Input

    Now that you've got your scene architected for camera rotation, turning input events into rotation is pretty easy. Just how easy depends on what kind of control you're after:

    • Looking for arcball rotation? (It's great for direct manipulation, since you can feel like you're physically pushing a point on the 3D object.) There are some questions and answers about that already on SO -- most of them use GLKQuaternion. (UPDATE: GLK types are "sorta" available in Swift 1.2 / Xcode 6.3. Prior to those versions you can do your math in ObjC via a bridging header.)
    • For a simpler alternative, you can just map the x and y axes of your gesture to the yaw and pitch angles of your node. It's not as spiffy as arcball rotation, but it's pretty easy to implement -- all you need to do is work out a points-to-radians conversion that covers the amount of rotation you're after.

    Either way, you can skip some of the gesture recognizer boilerplate and gain some handy interactive behaviors by using UIScrollView instead. (Not that there isn't usefulness to sticking with gesture recognizers -- this is just an easily implemented alternative.)

    Drop one on top of your SCNView (without putting another view inside it to be scrolled) and set its contentSize to a multiple of its frame size... then during scrolling you can map the contentOffset to your 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
    }
    

    On the one hand, you have to do a bit more work for infinite scrolling if you want to spin endlessly in one or both directions. On the other, you get nice scroll-style inertia and bounce behaviors.

    这篇关于旋转SCNCamera节点,查看假想球体周围的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆