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

如何用UIPanGestureRecognizer实现SCNNode绕自身中心旋转并修复形变

问题原因分析
  • 核心问题是旋转轴未做归一化处理:simd_quatf(angle:axis:) 构造器要求传入的axis必须是单位向量,你当前代码直接用平移量的x、y值作为轴分量,轴的长度随滑动距离变化,生成的四元数不是单位四元数,应用到节点上时会破坏节点变换矩阵的正交性,最终导致模型被拉伸、扭曲。
  • 硬编码的旋转角度计算逻辑存在边界异常:你固定了radius=600且乘以3倍系数,当滑动距离超过1200时,asin的入参(panTranslationDistance/2)/radius会超出[-1,1]的合法范围,返回无效的NaN值,生成的四元数完全异常,直接导致模型变形。
  • 相对旋转的累计误差:多次调用simdLocalRotate时,若每次的四元数都存在微小误差,长期累计会导致节点的变换矩阵附带非预期的缩放值,进一步加剧变形问题。
修复方案

首先调整角度计算逻辑,替换容易出问题的asin计算方式,同时对旋转轴做归一化处理,修复后的代码如下:

private func rotateModels(with startingLocation: CGPoint, endingLocation: CGPoint, node: SCNNode, sensitivity: Float = 0.005) {
    let panTranslation = CGPoint(x: endingLocation.x - startingLocation.x, y: endingLocation.y - startingLocation.y)
    // 滑动距离为0时直接返回,避免后续计算异常
    guard abs(panTranslation.x) > 0 || abs(panTranslation.y) > 0 else { return }
    
    // 计算旋转轴并归一化
    var axis = simd_float3(x: Float(-panTranslation.y), y: Float(panTranslation.x), z: 0)
    axis = simd_normalize(axis)
    
    // 用滑动距离乘以灵敏度系数计算旋转角度,避免asin的边界问题
    let panDistance = sqrt(pow(panTranslation.x, 2) + pow(panTranslation.y, 2))
    let angle = Float(panDistance) * sensitivity
    
    // 应用旋转,也可以替换为simdOrientation = simd_mul(simd_quatf(angle: angle, axis: axis), node.simdOrientation) 减少累计误差
    node.simdLocalRotate(by: simd_quatf(angle: angle, axis: axis))
}

如果需要进一步避免旋转累计带来的变换误差,可以在初始化节点时记录节点的初始旋转值,每次滑动时基于初始值叠加旋转量,而不是每次相对当前状态旋转。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 19:27:08