You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SceneKit中无需虚拟SCNNode实现两个欧拉角相加的方法

欧拉角相加的优化实现(无需虚拟SCNNode)

问题描述

我需要实现两个欧拉角的相加操作。目前的做法是将欧拉角转换为旋转矩阵后相乘,但最终从旋转矩阵还原欧拉角时,不得不依赖一个虚拟SCNNode,这感觉不够规范。我编写了如下代码:

import SceneKit
import Spatial

// A helper function
func eulerToRotation(eulerAngles: SCNVector3) -> SCNVector4 {
  let rotation = Rotation3D(eulerAngles: .init(angles: [eulerAngles.x, eulerAngles.y, eulerAngles.z], order: .xyz))
  return SCNVector4(
      simd_double4(rotation.axis.vector, rotation.angle.radians)
  )}

func addEulerAngles(e1: SCNVector3, e2: SCNVector3) -> SCNVector3 {
  let r1 = eulerToRotation(eulerAngles: e1), r2 = eulerToRotation(eulerAngles: e2)
  let m1 = SCNMatrix4MakeRotation(r1.w, r1.x, r1.y, r1.z), m2 = SCNMatrix4MakeRotation(r2.w, r2.x, r2.y, r2.z)
  let m = SCNMatrix4Mult(m1, m2)
  
  // Hacky here
  let node = SCNNode()
  node.transform = m
  return node.eulerAngles
}

请问有没有不使用该虚拟SCNNode的更优实现方式?

优化方案

可以直接利用simd库的四元数(simd_quatd)处理旋转组合,无需借助虚拟SCNNode,代码更高效且逻辑更清晰。

核心实现代码

import SceneKit
import Spatial

// 辅助函数:欧拉角转四元数(比转旋转矩阵更高效)
func eulerToQuaternion(eulerAngles: SCNVector3) -> simd_quatd {
    return simd_quatd(rotation: Rotation3D(eulerAngles: .init(angles: [eulerAngles.x, eulerAngles.y, eulerAngles.z], order: .xyz)))
}

// 优化后的欧拉角相加函数
func addEulerAngles(e1: SCNVector3, e2: SCNVector3) -> SCNVector3 {
    // 将两个欧拉角转成四元数
    let q1 = eulerToQuaternion(eulerAngles: e1)
    let q2 = eulerToQuaternion(eulerAngles: e2)
    
    // 复合旋转:先应用e1的旋转,再应用e2的旋转,四元数相乘顺序为q2 * q1
    let combinedQuaternion = q2 * q1
    
    // 将复合四元数转回欧拉角(XYZ顺序,和SCNNode.eulerAngles逻辑一致)
    let euler = combinedQuaternion.eulerAngles
    return SCNVector3(euler.x, euler.y, euler.z)
}

补充:若坚持使用旋转矩阵的实现方式

如果需要保留旋转矩阵的处理逻辑,也可以直接从矩阵转四元数再转欧拉角,无需虚拟节点:

func addEulerAnglesWithMatrix(e1: SCNVector3, e2: SCNVector3) -> SCNVector3 {
    let q1 = eulerToQuaternion(eulerAngles: e1)
    let q2 = eulerToQuaternion(eulerAngles: e2)
    
    // 四元数转矩阵后相乘
    let combinedMatrix = simd_double4x4(q2) * simd_double4x4(q1)
    
    // 矩阵转四元数再转欧拉角
    let combinedQuaternion = simd_quatd(combinedMatrix)
    let euler = combinedQuaternion.eulerAngles
    return SCNVector3(euler.x, euler.y, euler.z)
}

关键说明

  • 四元数处理旋转的效率高于旋转矩阵,且能避免欧拉角的万向锁问题(中间用四元数复合更稳定)。
  • 旋转顺序需注意:欧拉角相加对应先执行e1的旋转,再执行e2的旋转,因此四元数相乘顺序为q2 * q1(四元数的右乘特性决定),和矩阵相乘的m2 * m1逻辑一致。

内容的提问来源于stack exchange,提问作者OMGPOP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:26:12