SCNNode Z 旋转轴保持不变,而 X 和 Y 轴在节点旋转时发生变化
SCNNode Z-rotation axis stays constant, while X and Y axes change when node is rotated
我有一个用户可以通过平移手势旋转的节点。用户可以 select X、Y 或 Z 轴和平移,节点将围绕该轴旋转。
问题:节点的正面正对着摄像头。假设用户向右平移并绕 Y 轴旋转节点。节点的正面现在面向右侧。如果用户切换到 X 轴并向下平移,则节点的正面将从其初始的右向方向向下旋转(或从用户的角度顺时针旋转)。这是期望的行为。当用户切换到 Z 旋转时,问题就出现了。如果用户切换到 Z 轴旋转并向右平移,则节点将向下旋转(从用户的角度来看是顺时针方向)。
本质上,节点的 Z 轴始终不变,永远不会偏离其初始方向,但 X 和 Y 轴会发生变化,受其他轴旋转的影响。
谁能解释一下这是什么原因造成的?
下面是我用来旋转节点的代码:
let translation = sender.translation(in: sceneView)
var newAngleX = Float(translation.y)*Float((Double.pi)/180.0)
var newAngleY = Float(translation.x)*Float((Double.pi)/180.0)
var newAngleZ = Float(translation.x)*Float((Double.pi)/180.0)
if axisSelected == "x" {
newAngleX += currentAngleX
node.eulerAngles.x = newAngleX
if(sender.state == .ended) {
currentAngleX = newAngleX
}
}
if axisSelected == "y" {
newAngleY += currentAngleY
node.eulerAngles.y = newAngleY
if(sender.state == .ended) {
currentAngleY = newAngleY
}
}
if axisSelected == "z" {
newAngleZ += currentAngleZ
node.eulerAngles.z = newAngleZ
if(sender.state == .ended) {
currentAngleZ = newAngleZ
}
}
正如我之前在 your post 中所写,这是一个 Gimbal Lock
问题。 Gimbal Lock是三维机构失去一个DoF。 SceneKit中有两个变量:eulerAngles
(固有欧拉角,以弧度表示,表示围绕x-y-z轴旋转3圈的序列,顺序如下:Z
-Y
-X
or roll
, yaw
, pitch
) and orientation
(node's orientation, expressed as quaternion).
为了摆脱 ARKit 和 SceneKit 中的 gimbal lock 你需要使用单位四元数,其分量满足等式:
(x * x) + (y * y) + (z * z) + (w * w) == 1
要正确应用四元数,您还需要使用此结构的第四个组件 (w
)。四元数用 SCNVector4 表示,其中 x
-y
-z
值是归一化分量,w
字段包含旋转角度,单位为弧度,或扭矩大小,单位为牛顿-米)。
万向节锁定 当轴(三个万向节中的两个)被驱动为平行配置时发生。
我有一个用户可以通过平移手势旋转的节点。用户可以 select X、Y 或 Z 轴和平移,节点将围绕该轴旋转。
问题:节点的正面正对着摄像头。假设用户向右平移并绕 Y 轴旋转节点。节点的正面现在面向右侧。如果用户切换到 X 轴并向下平移,则节点的正面将从其初始的右向方向向下旋转(或从用户的角度顺时针旋转)。这是期望的行为。当用户切换到 Z 旋转时,问题就出现了。如果用户切换到 Z 轴旋转并向右平移,则节点将向下旋转(从用户的角度来看是顺时针方向)。
本质上,节点的 Z 轴始终不变,永远不会偏离其初始方向,但 X 和 Y 轴会发生变化,受其他轴旋转的影响。
谁能解释一下这是什么原因造成的?
下面是我用来旋转节点的代码:
let translation = sender.translation(in: sceneView)
var newAngleX = Float(translation.y)*Float((Double.pi)/180.0)
var newAngleY = Float(translation.x)*Float((Double.pi)/180.0)
var newAngleZ = Float(translation.x)*Float((Double.pi)/180.0)
if axisSelected == "x" {
newAngleX += currentAngleX
node.eulerAngles.x = newAngleX
if(sender.state == .ended) {
currentAngleX = newAngleX
}
}
if axisSelected == "y" {
newAngleY += currentAngleY
node.eulerAngles.y = newAngleY
if(sender.state == .ended) {
currentAngleY = newAngleY
}
}
if axisSelected == "z" {
newAngleZ += currentAngleZ
node.eulerAngles.z = newAngleZ
if(sender.state == .ended) {
currentAngleZ = newAngleZ
}
}
正如我之前在 your post 中所写,这是一个 Gimbal Lock
问题。 Gimbal Lock是三维机构失去一个DoF。 SceneKit中有两个变量:eulerAngles
(固有欧拉角,以弧度表示,表示围绕x-y-z轴旋转3圈的序列,顺序如下:Z
-Y
-X
or roll
, yaw
, pitch
) and orientation
(node's orientation, expressed as quaternion).
为了摆脱 ARKit 和 SceneKit 中的 gimbal lock 你需要使用单位四元数,其分量满足等式:
(x * x) + (y * y) + (z * z) + (w * w) == 1
要正确应用四元数,您还需要使用此结构的第四个组件 (w
)。四元数用 SCNVector4 表示,其中 x
-y
-z
值是归一化分量,w
字段包含旋转角度,单位为弧度,或扭矩大小,单位为牛顿-米)。
万向节锁定 当轴(三个万向节中的两个)被驱动为平行配置时发生。