ARKit 无法在垂直平面上正确旋转 SCNNode
ARKit cannot rotate a SCNNode correctly on a vertical plane
我想旋转一个SCNNode,它是一个绘画图像
我可以在地板上旋转它,但我不能在墙上正确旋转它。
(我正在使用 UIRotationGestureRecognizer)
...
func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
...
// Add the painting
let newPaintingNode = SCNNode(geometry: planeGeometry)
newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
newPaintingNode.eulerAngles = SCNVector3(newPaintingNode.eulerAngles.x + (-Float.pi / 2), newPaintingNode.eulerAngles.y, newPaintingNode.eulerAngles.z)
newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
self.paintingNode = newPaintingNode
self.currentAngleY = newPaintingNode.eulerAngles.y
self.currentAngleZ = newPaintingNode.eulerAngles.z
augmentedRealityView.scene.rootNode.addChildNode(self.paintingNode!)
grid.removeFromParentNode()
}
...
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let currentNode = self.paintingNode {
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
if self.paintingAlignment == "horizontal" {
log.verbose("rotate horizontal!")
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed {
currentNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleY = currentNode.eulerAngles.y
}
} else if self.paintingAlignment == "vertical" {
log.verbose("rotate vertical!")
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.z
if gesture.state == .changed {
currentNode.eulerAngles.z = currentAngleZ + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleZ = currentNode.eulerAngles.z
}
}
}
}
有谁知道如何在墙上正确旋转它?谢谢!
您正在代码中使用 eulerAngles
实例 属性:
var eulerAngles: SCNVector3 { get set }
根据 Apple 文档:
SceneKit applies eulerAngles
rotations relative to the node’s pivot property in the reverse order of the components: first roll
(Z), then yaw
(Y), then pitch
(X).
...但是三分量旋转会导致 Gimbal Lock.
Gimbal lock is the loss of one degree of freedom in a three-dimensional, three-gimbal mechanism that occurs when the axes of two of the three gimbals are driven into a parallel configuration, "locking" the system into rotation in a degenerate two-dimensional space.
所以你需要使用四分量旋转属性:
var rotation: SCNVector4 { get set }
The four-component rotation vector specifies the direction of the rotation axis in the first three components (XYZ) and the angle of rotation, expressed in radians
, in the fourth (W).
currentNode.rotation = SCNVector4(x: 1,
y: 0,
z: 0,
w: -Float.pi / 2)
如果您想了解更多关于 SCNVector4
结构和四分量旋转(及其以弧度表示的 W 分量)的信息,请查看 and THIS POST。
P.S.
在 RealityKit 框架中,您需要使用 SIMD4<Float>
通用结构或 simd_float4
类型别名而不是 SCNVector4
结构。
var rotation: simd_float4 { get set }
我尝试了 SCNNode eulerAngles
和 SCNNode rotation
但我没有运气...
我想那是因为我在添加绘画时调用了 SCNNode.transform()
。稍后我修改SCNNode.eulerAngles
并调用SCNNode.rotation()
时,它们可能会影响第一个transform
。
我终于使用 SCNMatrix4Rotate
而不是 eulerAngles
和 rotation
让它工作了。
func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
...
let newPaintingNode = SCNNode(geometry: planeGeometry)
newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
newPaintingNode.transform = SCNMatrix4Rotate(newPaintingNode.transform, -Float.pi / 2.0, 1.0, 0.0, 0.0)
newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
self.paintingNode = newPaintingNode
...
}
...
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let _ = self.paintingNode {
// Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
if gesture.state == .began {
self.rotationZ = rotation
}
if gesture.state == .changed {
let diff = rotation - self.rotationZ
self.paintingNode?.transform = SCNMatrix4Rotate(self.paintingNode!.transform, diff, 0.0, 0.0, 1.0)
self.rotationZ = rotation
}
}
}
上面的代码在垂直和水平平面上都有效。
我想旋转一个SCNNode,它是一个绘画图像
我可以在地板上旋转它,但我不能在墙上正确旋转它。
(我正在使用 UIRotationGestureRecognizer)
...
func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
...
// Add the painting
let newPaintingNode = SCNNode(geometry: planeGeometry)
newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
newPaintingNode.eulerAngles = SCNVector3(newPaintingNode.eulerAngles.x + (-Float.pi / 2), newPaintingNode.eulerAngles.y, newPaintingNode.eulerAngles.z)
newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
self.paintingNode = newPaintingNode
self.currentAngleY = newPaintingNode.eulerAngles.y
self.currentAngleZ = newPaintingNode.eulerAngles.z
augmentedRealityView.scene.rootNode.addChildNode(self.paintingNode!)
grid.removeFromParentNode()
}
...
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let currentNode = self.paintingNode {
//1. Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
if self.paintingAlignment == "horizontal" {
log.verbose("rotate horizontal!")
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
if gesture.state == .changed {
currentNode.eulerAngles.y = currentAngleY + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleY = currentNode.eulerAngles.y
}
} else if self.paintingAlignment == "vertical" {
log.verbose("rotate vertical!")
//2. If The Gesture State Has Changed Set The Nodes EulerAngles.z
if gesture.state == .changed {
currentNode.eulerAngles.z = currentAngleZ + rotation
}
//3. If The Gesture Has Ended Store The Last Angle Of The Cube
if(gesture.state == .ended) {
currentAngleZ = currentNode.eulerAngles.z
}
}
}
}
有谁知道如何在墙上正确旋转它?谢谢!
您正在代码中使用 eulerAngles
实例 属性:
var eulerAngles: SCNVector3 { get set }
根据 Apple 文档:
SceneKit applies
eulerAngles
rotations relative to the node’s pivot property in the reverse order of the components: firstroll
(Z), thenyaw
(Y), thenpitch
(X).
...但是三分量旋转会导致 Gimbal Lock.
Gimbal lock is the loss of one degree of freedom in a three-dimensional, three-gimbal mechanism that occurs when the axes of two of the three gimbals are driven into a parallel configuration, "locking" the system into rotation in a degenerate two-dimensional space.
所以你需要使用四分量旋转属性:
var rotation: SCNVector4 { get set }
The four-component rotation vector specifies the direction of the rotation axis in the first three components (XYZ) and the angle of rotation, expressed in
radians
, in the fourth (W).
currentNode.rotation = SCNVector4(x: 1,
y: 0,
z: 0,
w: -Float.pi / 2)
如果您想了解更多关于 SCNVector4
结构和四分量旋转(及其以弧度表示的 W 分量)的信息,请查看
P.S.
在 RealityKit 框架中,您需要使用 SIMD4<Float>
通用结构或 simd_float4
类型别名而不是 SCNVector4
结构。
var rotation: simd_float4 { get set }
我尝试了 SCNNode eulerAngles
和 SCNNode rotation
但我没有运气...
我想那是因为我在添加绘画时调用了 SCNNode.transform()
。稍后我修改SCNNode.eulerAngles
并调用SCNNode.rotation()
时,它们可能会影响第一个transform
。
我终于使用 SCNMatrix4Rotate
而不是 eulerAngles
和 rotation
让它工作了。
func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
...
let newPaintingNode = SCNNode(geometry: planeGeometry)
newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
newPaintingNode.transform = SCNMatrix4Rotate(newPaintingNode.transform, -Float.pi / 2.0, 1.0, 0.0, 0.0)
newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
self.paintingNode = newPaintingNode
...
}
...
@objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
if let _ = self.paintingNode {
// Get The Current Rotation From The Gesture
let rotation = Float(gesture.rotation)
if gesture.state == .began {
self.rotationZ = rotation
}
if gesture.state == .changed {
let diff = rotation - self.rotationZ
self.paintingNode?.transform = SCNMatrix4Rotate(self.paintingNode!.transform, diff, 0.0, 0.0, 1.0)
self.rotationZ = rotation
}
}
}
上面的代码在垂直和水平平面上都有效。