旋转对象以匹配向量

Posted

技术标签:

【中文标题】旋转对象以匹配向量【英文标题】:Rotating an object to match a vector 【发布时间】:2015-04-17 23:36:13 【问题描述】:

我认为我做这件事的时间最长,但我相信我做这件事的方式可能非常错误。

我有一个带有世界地形的 3D 引擎,并且可以将对象放置在地面上,当它们被放置时,它们会定向到它们所在的地形(旋转)。我这样做的方式是这样的:

D3DXVECTOR3 tNormal = GetNormalFromFile( ... );  //Get the terrain's normal

placeItemData.placeMesh.meshRot.z = (acos(tNormal.x) - (D3DX_PI/2));
placeItemData.placeMesh.meshRot.x = -(acos(tNormal.z) - (D3DX_PI/2)); 

我将旋转存储为弧度中的向量:x,y,z 旋转,其中 Y 向上。所以我计算X和Z轴的旋转并相应地旋转。玩家可以在放置对象之前手动围绕 Y 轴旋转对象,据说所有这些都运行良好。

随着我不断改进我的物品放置代码,我意识到这不适用于近乎垂直的平面。例如,尝试将图片放在墙上。此代码似乎可以将物品放置在接近水平(平坦)的地形上,但对于垂直墙来说,旋转距离很远。

我在这里遗漏了一个简单的计算吗?我是否需要以某种方式计算 y 轴值?我也尝试使用 atan2 函数计算旋转:

placeItemData.placeMesh.meshRot.z = (atan2(finalNormal.x,finalNormal.y) );
placeItemData.placeMesh.meshRot.x = (atan2(finalNormal.z,finalNormal.y) );

但我遇到了类似的问题。

我需要使用四元数进行这些计算吗?我能否再次轻松地将最终旋转值存储在 3D 向量 (x,y,z) 中?

感谢您的帮助!

更新 1

这是使用此代码的图像:

meshRot.z = - atan2(sqrt(finalNormal.y^2 + finalNormal.z^2), finalNormal.x);
meshRot.x = - atan2(finalNormal.y, finalNormal.z);

(来源:crystaldragon.com)

当您将鼠标悬停在石头上时,会计算三角形的法线(对于图片,石头面的法线是 (0.637, -0.014, 0.770) )。当然你在石头里看到的木块,应该是直接铺在石头边上的(就像挂成画一样)。

更新 2

通过在我的世界中测试位于 z 轴上的“墙”,我似乎让代码适用于所有 y=0 的情况。我可以将对象放置在所有垂直墙壁上,但不能放置在墙壁的水平(顶部)上。

这是当前代码:

placeItemData.placeMesh.meshRot.x =  -(atan2(finalNormal.y, sqrt((finalNormal.x * finalNormal.x ) + (finalNormal.z * finalNormal.z)) ) + (D3DX_PI/2));
placeItemData.placeMesh.meshRot.z =  -(atan2(finalNormal.z, finalNormal.x ) + (D3DX_PI/2)); 

只要 y 法线的值为 0,对象就会附着在每个垂直的墙壁上,但不会附着在水平面上(因为 y 法线显然是非负数)。希望最后一步是找出我需要作为 y 值输入方程的最终计算,但我仍然想出失败的尝试。

【问题讨论】:

我知道旋转,但我不知道 Direct3D。旋转的两个值就足够了,但是当你使用meshrot 中的值来旋转对象时,你是用自己的代码来做还是调用 Direct3D 函数? 我只知道这将与atan2atan2(0, -x) 的不连续性有关。 这就是我做旋转矩阵的方式:D3DXMatrixRotationYawPitchRoll(&matRotate, 0.0f, meshRot.x, meshRot.z ); D3DXMatrixRotationYawPitchRoll(&matRotate2, meshRot.y, 0.0f, 0.0f ); D3DXMatrixMultiply(&matRotate, &matRotate2, &matRotate); 那张图真的不太清楚。我们说的是石头的哪一面?轴在哪里?那个黑色的斑点是木块吗?它应该有什么方向? 抱歉图片不佳。我试图说明的是算法没有与岩壁对齐,我给出的向量 (0.637, -0.014, 0.770) 是岩壁侧面的法线,物体(木块粘在上面) out) 应该对齐到。显然,旋转没有正确对齐该法线向量。 【参考方案1】:

对于接近水平(平坦)的地形,地形位于 X-Z 平面上,因此法线的 y 值很可能是 1.0,这应该会给您不使用 y 法线的正确旋转。

但是,当向墙壁添加对象时,需要 y 法线来计算旋转的 y 分量。您说玩家可以在放置对象之前手动围绕 Y 轴旋转对象。

那是您使用法线的 y 值计算旋转的 y 分量的地方吗?如果没有,请确保您也在计算它。否则,您将在 2D 中计算旋转,并以某种方式将其外推到 3D。

【讨论】:

所以对于展示位置,我目前没有在项目展示位置的任何计算中使用 y 组件。 y 旋转只是在事后添加为对象自己的“单独”旋转。我把轮换代码放到了上面的注释块里。【参考方案2】:

我会推荐四元数。你可以在没有它们的情况下让它工作,但你的代码必须对零进行大量检查,如果你尝试为任何东西设置动画,就会出现不连续性。四元数有一点学习曲线,但一旦你有了:

一种旋转方式

从笛卡尔(或极坐标或其他)到四元数的转换

从四元数到笛卡尔(或其他)的转换

使用起来非常简单,它们是 3D 操作的正确选择。

老实说,您不必了解数学即可使其工作;你只需要能够遵循四元数的数学规则。

【讨论】:

谢谢。如果我不能让更“传统”的方法发挥作用,我可能会走这条路。【参考方案3】:

我不知道 Direct3D,但是通过快速查看文档,看起来这就是您想要的:

meshRot.z = - atan2(sqrt(finalNormal.y^2 + finalNormal.z^2), finalNormal.x);
meshRot.x = - atan2(finalNormal.y, finalNormal.z);

(我不知道这是否是正确的语法,例如 sqrt,但你知道我在做什么。)

关于 Y 轴的旋转(“偏航”)可以留给用户 - 它对应于将雕像从朝北旋转到朝东,或者将一幅画倒挂在墙上。

我假设旋转是关​​于对象固定轴的。如果不是这种情况,那么解决方案将看起来完全不同。试一试,如果不起作用,我们可以尝试几个简单的测试用例来确定它。

【讨论】:

非常感谢您的帮助!我已对我的原始帖子添加了更新,显示了代码的当前结果并附有一些解释。我也会继续做一些研究。

以上是关于旋转对象以匹配向量的主要内容,如果未能解决你的问题,请参考以下文章

特征点匹配,并求旋转矩阵R和位移向量t

第八章:矩阵和线性变换

旋转几何图形以对齐方向向量

从 OpenCV 中的旋转矩阵和平移向量中获取旋转轴

OpenGL使旋转函数给出向量

Three.js Object3d 圆柱体旋转以对齐向量