Rotate SCNCamera node looking at an object around an imaginary sphere(旋转 SCNCamera 节点,观察假想球体周围的对象)
问题描述
我在位置 (30,30,30) 有一个 SCNCamera,在位置 (0,0,0) 的对象上有一个 SCNLookAtConstraint.我正在尝试使用 A UIPanGestureRecognizer 让相机围绕假想球体上的对象旋转,同时保持相机和对象之间的半径.我假设我应该使用四元数投影,但我在这方面的数学知识很糟糕.我已知的变量是 x &y 翻译 + 我试图保持的半径.我已经用 Swift 编写了这个项目,但是 Objective-C 中的答案同样会被接受(希望使用标准的 Cocoa Touch 框架).
地点:
private var cubeView : SCNView!;私人 var cubeScene : SCNScene!;私有 var cameraNode : SCNNode!;
这是我设置场景的代码:
//设置 SCNViewcubeView = SCNView(frame: CGRectMake(0, 0, self.width(), 175));cubeView.autoenablesDefaultLighting = YES;self.addSubview(cubeView);//设置场景立方体场景 = SCNScene();cubeView.scene = cubeScene;//设置相机让相机 = SCNCamera();camera.usesOrthographicProjection = YES;camera.orthographicScale = 9;相机.zNear = 0;相机.zFar = 100;cameraNode = SCNNode();cameraNode.camera = 相机;cameraNode.position = SCNVector3Make(30, 30, 30)cubeScene.rootNode.addChildNode(cameraNode)//设置一个目标对象let box = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 0);让 boxNode = SCNNode(几何:框)cubeScene.rootNode.addChildNode(boxNode)//对相机施加约束让 targetNode = SCNLookAtConstraint(target: boxNode);targetNode.gimbalLockEnabled = YES;cameraNode.constraints = [targetNode];//添加手势识别器让手势= UIPanGestureRecognizer(目标:自我,动作:panDetected:");cubeView.addGestureRecognizer(手势);
这里是手势识别器处理的代码:
私有变量位置:CGPoint!;内部函数 panDetected(手势:UIPanGestureRecognizer){开关(手势状态){案例 UIGestureRecognizerState.Began:位置 = CGPointZero;案例 UIGestureRecognizerState.Changed:让 aPosition = gesture.translationInView(cubeView);让 delta = CGPointMake(aPosition.x-position.x, aPosition.y-position.y);//???不知道...位置 = 一个位置;默认:休息}}
谢谢!
将您的问题分解为子问题可能会有所帮助.
设置场景
首先,考虑如何组织您的场景以实现您想要的那种运动.您谈到移动相机,就好像它连接到一个不可见的球体一样.使用这个想法!与其尝试计算将您的 cameraNode.position
设置到假想球体上的某个点,不如想想如果将相机连接到球体上,您将如何移动相机.也就是说,只需旋转球体即可.
如果您想将球体与场景内容的其余部分分开旋转,请将其附加到单独的节点.当然,您实际上并不需要插入 已经在 SO 上——他们中的大多数使用 GLKQuaternion
.(更新: GLK 类型在 Swift 1.2/Xcode 6.3 中排序"可用.在这些版本之前,您可以通过桥接头在 ObjC 中进行数学运算.)
无论哪种方式,您都可以通过使用 UIScrollView
来跳过一些手势识别器样板并获得一些方便的交互行为.(并不是说坚持使用手势识别器没有用处——这只是一个易于实现的替代方案.)
在您的 SCNView
顶部放置一个(不要在其中放置另一个要滚动的视图)并将其 contentSize
设置为其帧大小的倍数...然后在滚动期间,您可以将 contentOffset
映射到您的 eulerAngles
:
func scrollViewDidScroll(scrollView: UIScrollView) {让 scrollWidthRatio = Float(scrollView.contentOffset.x/scrollView.frame.size.width)让 scrollHeightRatio = Float(scrollView.contentOffset.y/scrollView.frame.size.height)cameraOrbit.eulerAngles.y = Float(-2 * M_PI) * scrollWidthRatiocameraOrbit.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 ofcameraOrbit
, its own position stays constant -- the camera moves due to the rotation ofcameraOrbit
. - 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 节点,观察假想球体周围的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:旋转 SCNCamera 节点,观察假想球体周围的对象
基础教程推荐
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01