Scene Kit 的枢轴矩阵的语义是啥?

Posted

技术标签:

【中文标题】Scene Kit 的枢轴矩阵的语义是啥?【英文标题】:What are the semantics of Scene Kit's pivot matrix?Scene Kit 的枢轴矩阵的语义是什么? 【发布时间】:2013-03-22 03:59:00 【问题描述】:

我正在努力理解 Scene Kit 使用节点的变换相关属性的语义。特别是,节点上的 pivot 属性不是一个点,而是一个完整的变换矩阵,我在 Collada 规范或其他文档中没有找到任何可比较的参考。

有没有人有使用 Scene Kit 中的枢轴矩阵或具有类似语义的属性的经验?有人可以提供有关如何使用它的简要说明/示例或指向相关文档的指针吗?它看起来是一种潜在有用的方法,但如果可以避免的话,我宁愿不必对其进行逆向工程。

编辑:根据 OMZ 的响应,看起来枢轴可能只是变换树中的另一层。但是,如果是这样的话,它与仅仅添加一个中间节点有什么不同呢?确定在何处应用哪些转换的依据是什么?

【问题讨论】:

【参考方案1】:

我尝试了@omz 的想法。但是得到一些相互矛盾的结果,所以我用一些有趣的结果进一步研究了这个。

答案是主变换的逆被应用为主变换的子变换。这提供了与提供中间父节点完全不同的语义。

具体来说,如果我从一个具有变换 P 的父节点和一个具有变换 C 的子节点开始,它们组合起来形成一个世界变换 W = CATransform3DConcat(C, P)。相同的世界变换 W 是通过将父节点的轴心设置为 C 的倒数,而不是对子节点设置变换来获得的。

这种方法似乎确实提供了一些额外的实用性,因为它不能通过插入中间节点来复制,也不能在应用之前被反转。正如 Hal Mueller 指出的那样,文档确实说枢轴会影响旋转和缩放以及位置,但对影响的性质保持沉默。

这是测试这个结论的结果:

Set parent rotation and node position.

Transform:
  1.00,  0.00,  0.00,  0.00 
  0.00,  1.00,  0.00,  0.00 
  0.00,  0.00,  1.00,  0.00 
  1.00,  2.00,  3.00,  1.00 

Pivot:
  1.00,  0.00,  0.00,  0.00 
  0.00,  1.00,  0.00,  0.00 
  0.00,  0.00,  1.00,  0.00 
  0.00,  0.00,  0.00,  1.00 

World Transform:
  1.00,  0.00,  0.00,  0.00 
  0.00,  0.00,  1.00,  0.00 
  0.00, -1.00,  0.00,  0.00 
  1.00, -3.00,  2.00,  1.00 
2013-04-04 13:49:55.453 Model Importer[43504:303] 

CATransform3DConcat(node.transform, node.parentNode.transform)]
  1.00,  0.00,  0.00,  0.00 
  0.00,  0.00,  1.00,  0.00 
  0.00, -1.00,  0.00,  0.00 
  1.00, -3.00,  2.00,  1.00 
2013-04-04 13:49:55.454 Model Importer[43504:303] 

Set parent rotation and parent.pivot to inverse position.

Transform:
  1.00,  0.00,  0.00,  0.00 
  0.00,  1.00,  0.00,  0.00 
  0.00,  0.00,  1.00,  0.00 
  0.00,  0.00,  0.00,  1.00 

Pivot:
  1.00,  0.00,  0.00,  0.00 
  0.00,  1.00,  0.00,  0.00 
  0.00,  0.00,  1.00,  0.00 
  0.00,  0.00,  0.00,  1.00 

World Transform:
  1.00,  0.00,  0.00,  0.00 
  0.00,  0.00,  1.00,  0.00 
  0.00, -1.00,  0.00,  0.00 
  1.00, -3.00,  2.00,  1.00 

代码如下:

    // Verification
    SCNVector3 v3 = 1.0, 2.0, 3.0;
    SCNVector4 v4 = 1.0, 0, 0, M_PI/2.0;
    node.parentNode.rotation = v4;
    node.position = v3;
    NSLog(@"\n\nSet parent rotation and node position.%@",
          [self transformsForNodeToString:node]);
    //
    // Verify match with CATransform3DConcat
    NSLog(@"\n\nCATransform3DConcat(node.transform, node.parentNode.transform)]%@",
          [self transformToString:CATransform3DConcat(node.transform, node.parentNode.transform)]);
    //
    // Clear the child node transform and put the inverse in the parent's pivot.
    CATransform3D position = node.transform;
    CATransform3D inversePosition = CATransform3DInvert(position);

    node.transform = CATransform3DIdentity;
    node.parentNode.pivot = inversePosition;
    NSLog(@"\n\nSet parent rotation and parent.pivot to inverse position.%@",
          [self transformsForNodeToString:node]);
    node.parentNode.pivot = CATransform3DIdentity;
    node.parentNode.transform = CATransform3DIdentity;


+ (NSString*)transformsForNodeToString: (SCNNode*)node 
    NSString* result = @"\n";
    result = [result stringByAppendingFormat:
          @"\nTransform:%@\nPivot:%@\nWorld Transform:%@",
          [self transformToString:node.transform],
          [self transformToString:node.pivot],
          [self transformToString:[node worldTransform]]];
    return result;


+ (NSString*)transformToString: (CATransform3D)transform 
    NSString* result = @"\n";
    result = [result stringByAppendingFormat:
              @" % .2f, % .2f, % .2f, % .2f \n", transform.m11, transform.m12, transform.m13, transform.m14];
    result = [result stringByAppendingFormat:
              @" % .2f, % .2f, % .2f, % .2f \n", transform.m21, transform.m22, transform.m23, transform.m24];
    result = [result stringByAppendingFormat:
              @" % .2f, % .2f, % .2f, % .2f \n", transform.m31, transform.m32, transform.m33, transform.m34];
    result = [result stringByAppendingFormat:
              @" % .2f, % .2f, % .2f, % .2f \n", transform.m41, transform.m42, transform.m43, transform.m44];
    return result;

欢迎评论!

【讨论】:

【参考方案2】:

我对 SceneKit 没有太多经验,但在 3D 建模应用程序中,枢轴是一个可以旋转的点(有时称为定位器)是很常见的。例如,这使您可以更轻松地使对象围绕自己的轴旋转(不一定对应于世界轴)。

对于未旋转的枢轴,矩阵将简单地为CATransform3DMakeTranslation(x, y, z)

编辑:我认为枢轴被指定为完整矩阵的事实仅仅是因为它是一种方便的数据结构并且与模型的其余部分一致。我想不出你想要缩放枢轴的情况,但是将其表示为矩阵可以很容易地使用通常的矩阵操作函数对其进行操作。

您当然可以将枢轴表示为另一个中间节点并获得相同的结果,但枢轴是表达对象中心的更“语义”方式,更适合交互式编辑。当您在建模应用程序中选择对象时,通常会将转换工具句柄定位在枢轴所在的位置,以便您可以围绕其“自然”中心旋转/缩放对象,而无需在节点层次结构中向上导航。

我真的找不到任何好的参考资料。出现的大部分内容似乎都涉及各种软件包中的具体实现细节。

【讨论】:

感谢您的回复!但是,当枢轴指定完整矩阵时,我仍然对语义感到困惑。我尝试在定位器上进行搜索,但是,就像枢轴一样,该术语的使用似乎模棱两可,没有一个与您所描述的或我在 Scene Kit 中看到的匹配。您是否知道任何链接或参考资料? 谢谢,@omz,这似乎是有道理的。我只是不想以一种会导致导入模型出现问题的方式开始使用它。我会尝试一些事情,看看是否能保持一致,然后跟进。 @omz 的 cmets 与属性 positionscalerotation 的文档一致:均相对于 pivot 指定。 @HalMueller, omz。谢谢你的帮助!我根据您的两个回答对此进行了深入研究。请查看我的回答以了解详细信息,如果您发现我的结论有任何问题,请告诉我。【参考方案3】:

我认为您会在 Core Animation 材料中找到一些相关示例。 SCNNodepivot 定义为 CATransform3D,在 Mac 和 ios 上的整个 Core Animation 中都使用它。

我自己对 Scene Kit 的研究还不够权威。但我发现this post by Brad Larson:“Core Animation 提供的用于操作 CALayers 的 CATransform3D 结构在结构上与 OpenGL 中的模型视图矩阵相同”。

GitHub 上还有一个很酷的可视化工具,https://github.com/honcheng/CATransform3D-Test。 Honcheng 的演示仅适用于 iPad,但我认为仍然很有帮助。

【讨论】:

感谢 GitHub 演示的链接。看起来它可能是一个很好的资源。但是,我知道转换通常是如何工作的,但我试图了解 Scene Kit 中节点的这一特定属性的意图,特别是因为我看不到它如何映射到 Collada 规范,就像 Scene 中的大多数其他功能一样套件做。

以上是关于Scene Kit 的枢轴矩阵的语义是啥?的主要内容,如果未能解决你的问题,请参考以下文章

推荐系统中的矩阵分解演变方式

旋转矩阵公式,是啥?

推荐算法之隐语义模型(LFM)矩阵分解梯度下降算法实现

推荐算法之隐语义模型(LFM)矩阵分解梯度下降算法实现

矩阵的几何意义是啥

梯度 海森矩阵 是啥学科