ARKit开发:如何将SCNTorus节点置于相机前方对齐显示正圆
问题根因
- 你之前用
node.eulerAngles.x = .pi / 2的方案,只是刚好匹配了竖屏持机、相机水平朝向这一个静态场景下的Torus朝向。 - 当设备在空间中移动、俯仰、屏幕滚转时,相机自身的旋转角度是实时变化的,硬编码的固定旋转值无法和相机当前的成像平面方向对齐,Torus的环面就会和相机视线形成夹角,最终从正圆变成椭圆。
实现方案
要让固定在hitTest锚点上的Torus永远显示为正圆,不需要做复杂的矩阵计算,直接对齐相机的实时旋转即可:
- SCNTorus的默认几何规则是:环面位于节点本地坐标系的XY平面,环的中心轴沿本地Z方向。
- SceneKit相机节点的默认坐标系规则是:Z轴垂直于相机成像平面,指向相机视线的正前方。
只要把相机当前的世界空间旋转直接赋值给Torus节点,两者的旋转方向就会完全匹配,Torus的平面会永远平行于相机成像面,不管设备姿态怎么变,看到的都是无变形的正圆。
修正后的可运行代码
把原来硬编码旋转的逻辑替换为相机旋转同步即可,位置依然用之前hitTest拿到的世界坐标,不会改动锚点位置:
@objc private func didDoubleTap(_ sender: UITapGestureRecognizer) { let point = sender.location(in: sceneView) guard let targetVector = getVector(for: point) else { return } guard let cameraNode = sceneView.pointOfView else { return } let geometry = SCNTorus(ringRadius: 0.01, pipeRadius: 0.001) let node = SCNNode(geometry: geometry) // 同步hitTest得到的世界锚点位置 node.simdWorldPosition = targetVector.simd // 同步相机的实时世界旋转,替换原来硬编码的欧拉角设置 node.simdWorldOrientation = cameraNode.simdWorldOrientation sceneView.scene.rootNode.addChildNode(node) animatePointingNode(node) }
补充优化:如果需要设备后续移动时,已经放置好的定位指针依然保持正圆形态,可以在场景每帧更新的回调中,遍历所有定位指针节点,重新同步一次相机的世界旋转即可。用四元数的旋转属性不会出现欧拉角的万向锁问题,对齐精度比手动算角度更高。
内容的提问来源于stack exchange,提问作者Nikolai Prokofev
相关产品推荐
相关产品推荐

