【问题标题】:How to align a face to a position in world space?如何将面与世界空间中的位置对齐?
【发布时间】:2013-04-21 06:16:51
【问题描述】:

我有一道数学题。假设我在某个世界位置有一张脸(有 3 个或 4 个顶点)。我想平移/旋转网格,使面部“朝上”并居中(0,y,0)。实现这一目标所需的公式是什么?

我可以使用 gui 来执行此操作(此示例只是 appx -90 度的 x 旋转),但是我需要通过脚本执行此操作,因此我需要知道如何在数学上完成此操作。

编辑: 我还应该注意,这些向量是我想要旋转的网格的一部分(原点为 (0,0,0)),直到 v1 位于位置 v2。

这是失败的伪代码:

v1 = vector(0,10,0)
v2 = vector(0,-10,0)

v1 = normalize(v1)
v2 = normalize(v2)

cross = normalize( v2.cross(v1) )  // (0,0,0)
angle = acos( v2.dot(v1) )  // 180

quat  = quaternion(cross,angle) // {w:1,x:0,y:0,z:0}

我原以为四元数会是这样的:{w:?,x:3.14159,y:0,z:0} 或 {w:?,x:0,y:0,z:3.14159}

【问题讨论】:

  • 您只是想沿轴对齐吗?如果您不关心它是否保持完全相同的大小,只需沿 y 轴平均顶点并将顶点的 y 值设置为该值。然后将x和z清0。
  • 我希望位置 a 的顶点通过旋转/平移网格移动到位置 b。
  • 没错,我确信下面的数学是正确的,但是把它变成一个函数......哦,男孩。
  • 四元数表示为 其中 表示角度(在本例中为 PI), 是您所依据的轴正在旋转。您期望的四元数看起来像 ,但它不会完全是这样,因为它没有标准化。
  • 啊想知道“w”是什么,呵呵谢谢。

标签: python math 3d blender mesh


【解决方案1】:

这个问题可以通过旋转矩阵或四元数来解决,但我建议使用旋转矩阵路线,因为您可以通过一次矩阵乘法同时解决所有点。

旋转矩阵:如果您知道要旋转的欧拉角是多少,那么rotation matrix 就是您的最佳选择。要形成旋转矩阵,请参阅链接的“基本旋转”部分。而不是知道什么是“向上”,您需要知道您希望旋转对象的程度。在这种情况下(从提供的照片来看),您希望围绕 global x 轴旋转 90 度(如果您希望围绕 local 轴旋转,您必须知道对象的当前方向。如果您需要 local 旋转,我可以在编辑中详细说明)。您的 global 旋转矩阵将是:

[1  0  0]
[0  0  1]
[0 -1  0]

我使用“基本旋转”部分中的 Rx(90) 矩阵计算了这一点。现在,在列向量中形成 3D 点。假设一个点位于 (0,0,1)。这个点直接就是鼻子所在的位置,所以我们希望转换后的点是 (0,1,0)。只需左乘旋转矩阵即可得到结果:

[1  0  0] [0] [0]
[0  0  1]*[0]=[1]
[0 -1  0] [1] [0]

请注意,在这种情况下,转换是相当微不足道的;我们只是简单地移动坐标(x 保持不变,y 取反,z 和 y 交换)。您可以通过水平连接所有初始坐标以形成一个 3xN 矩阵,然后左乘旋转矩阵来同时变换大量点。例如,让我们变换点 { (0,0,1), (0,1,0), (1,0,1), (0,0,-1) }:

[1  0  0] [0  0  1  0] [0  0  1  0]
[0  0  1]*[0  1  0  0]=[1  0  1  0]
[0 -1  0] [1  0  1 -1] [0 -1  0  1]

提醒一下,这个变换围绕全局原点旋转(如 (1,0,1) 点所示)。您必须减去坐标的质心,旋转,然后添加最终的平移坐标。

四元数:我可以在这里给出一个教程,但这通常被称为“轴角”符号;您可以使用它来创建一个旋转矩阵,该矩阵将围绕任意单位轴将您的点旋转指定角度。 Here 是一个很好的教程。让我知道我是否应该在编辑中详细说明。

编辑:响应添加的伪代码

如果叉积为 0,则线平行。旋转轴可以是垂直于任一输入的任何矢量(根据定义使其垂直于两者)。如果dot(v,p)==0,或者vx*p.x+vy*p.y+vz*pz==0且length(p)>0,则向量p被定义为垂直,所以我们可以任意选择满足这些方程的任何解。

v1 = vector(0,10,0)
v2 = vector(0,-10,0)

//Not necessary, since you will normalize the cross product result
//v1 = normalize(v1)
//v2 = normalize(v2)

cross = v2.cross(v1)  // (0,0,0) and possible divide by 0 if normalized here
if(length(cross)==0){ //either "==0" or "<thresh" where thresh is some very small number
   if(v.z!=0)
        cross = vector(1,1,-(v1.x+v1.y)/v1.z);
   else if(v.y!=0) //is z==0?  well here's an identical solution as long as y isn't 0
        cross = vector(1,-(v1.x+v1.z)/v.y,1);
   else //by this point, v1.x must be the only nonzero remaining point, otherwise it's a null vector
        cross = vector(-(v1.y+v1.z)/v.x,1,1);
}
cross=normalize(cross);
angle = acos( normalize(v2.dot(v1)) )  // 180

quat  = quaternion(cross,angle)

我不熟悉 python 代码,所以我添加了 C++ 等效代码。如果有人可以编辑这篇文章来更正它,那就太好了。

编辑:我没有看到您对 acos 的评论,对此感到抱歉。相应地更改了代码。

【讨论】:

  • 我真希望我能更了解这一点。我还在和这个作斗争。我正在使用编程语言(python)执行此操作,并且很难将您发布的内容转换为函数。我偶然发现了 angle = acos(dot(v1,v2))。这似乎有时会奏效。我从 (0,-1,0) 和 (0,1,0) => 180 得到正确的角度,但是当我做 (0,-2,0) 和 (0,2,0) 时,acos 函数会中断因为在那种情况下 dot(v1,v2) 不再是 0 - 1。Eck 我很困惑。我应该声明我根本不擅长这种数学 =[
  • 我能够通过事先对向量进行归一化来让 acos(dot(v1,v2)) 函数停止中断。但是由于某种原因,我不明白 v1 (0,1,0) 和 v2 (0,-1,0) 之间的四元数是 {w:1, x:0, y:0, z:0} 即使arcos(dot(v1,v2)) 正在返回 PI。我原以为 x 或 z 旋转都是 PI。
  • 如果您通过叉积找到旋转轴,您必须确保叉积不会导致 (0,0,0)(在您提供的示例中,确实如此!)。此外,请仔细检查以确保在计算四元数(在本例中为 PI/2)时将角度减 2。如果这些不是正确的修复,请尝试在 OP 的编辑中发布一些代码。
  • 对!这种情况下的叉积是 (0,0,0),这让我很困惑。
  • 编辑了答案以捕捉并行向量的情况,但它没有经过测试,所以它可能不完全正确,哈哈。
猜你喜欢
  • 2023-03-11
  • 2014-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-04
  • 1970-01-01
相关资源
最近更新 更多