给定表面法线,找到 3D 平面的旋转

Posted

技术标签:

【中文标题】给定表面法线,找到 3D 平面的旋转【英文标题】:Given a surface normal, find rotation for 3D Plane 【发布时间】:2011-01-06 23:27:05 【问题描述】:

所以我有一个由 2 个向量描述的 3D 平面:

P : 平面上的一个点 N : 平面的表面法线

我有一个非常大的扁平方形多边形,我想渲染它来代表这个平面。我可以轻松地将多边形平移到给定的点,但是我需要找到合适的旋转来应用,以使表面法线实际上是表面法线。

我尝试了其他提到的方法:

1) 取任意非平行向量 (V) 到法线 (N),并取叉积 (W1) 2)现在(W2)取(W1)和(N)的叉积,这是一个位于平面上的向量(V')

然后,我根据平面上的 (V') 生成一个旋转矩阵,这样我的多边形将与 (V') 对齐。这行得通,但很明显,这种方法整体上不能正常工作。多边形并非完全垂直于表面法线。

关于如何生成正确的旋转有什么想法吗?

【问题讨论】:

【参考方案1】:

关于旋转的一些有用的东西:

任何三个排列成行的正交向量都定义了到新基的转换(到该基的旋转)。 任何旋转的转置都是它的逆。 因此,排列为列的任意三个正交向量定义了从某个基础到您的“世界”参考系的旋转。

所以,问题是找到任何三个正交向量的集合并将它们排列为

| x1 x2 x3  0 |
| y1 y2 y3  0 |
| z1 z2 z3  0 |
|  0  0  0  1 |

这正是你描述的方法试图做的,如果它不起作用,那么你的实现就有问题。

我们显然可以将您的法线用作 (x1,y1,z1),但问题是系统对剩余的两个向量有无限多的解决方案(尽管知道其中一个会为您提供另一个,作为叉积)。下面的代码应该给出一个垂直于 (x1,y1,z1) 的稳定向量:

float normal[3] =  ... ;

int imin = 0;
for(int i=0; i<3; ++i)
    if(std::abs(normal[i]) < std::abs(normal[imin]))
        imin = i;

float v2[3] = 0,0,0;
float dt    = normal[imin];

v2[imin] = 1;
for(int i=0;i<3;i++)
    v2[i] -= dt*normal[i];

这基本上使用了格拉姆-施密特正交化,其维度已经与法线向量最正交。然后可以通过取normalv2 的叉积来获得v3。

您可能需要注意设置旋转,它是关于原点的,因此您需要在旋转后应用平移,它适用于列向量而不是行向量。如果您使用的是 OpenGL,请注意 OpenGL 以列主要顺序(而不是 C 的行主要顺序)获取数组,因此您可能需要转置。

恐怕我还没有测试过上面的内容,我只是从我不久前写的一些代码中找到了它,并根据你的问题对其进行了调整!希望我没有忘记任何细节。

编辑:我确实忘记了一些东西:)

上面的矩阵假设你的多边形法线是沿着 x 轴的,我有一个偷偷摸摸的怀疑它不会是,你需要做的就是把“法线”向量放在旋转的正确列中矩阵,以及其他两列中的 v2/v3。因此,如果多边形的法线沿 z 轴,则法线位于第 3 列,v2/v3 位于前两列。

抱歉,如果这引起任何混乱。

【讨论】:

出于随机原因,我发现自己重新审视了这个答案,当我选择最正交的维度时,我应该使用法线向量分量的绝对值 - 我已经在答案中更正了这一点。跨度> 如果法线不在任何轴上怎么办?相反,如果它类似于 ? 该算法将任意法线映射为沿着其中一个轴(或相反的方式),如果您想将法线旋转到另一个法线上,我会找到关于叉积的旋转两个法线中的一个。【参考方案2】:

不知道你用什么方法渲染,借用OpenSceneGraph's matrix:

void Matrix_implementation::makeLookAt(const Vec3d& eye,const Vec3d& center,const Vec3d& up)

    Vec3d f(center-eye);
    f.normalize();
    Vec3d s(f^up);
    s.normalize();
    Vec3d u(s^f);
    u.normalize();

    set(
        s[0],     u[0],     -f[0],     0.0,
        s[1],     u[1],     -f[1],     0.0,
        s[2],     u[2],     -f[2],     0.0,
        0.0,     0.0,     0.0,      1.0);

    preMultTranslate(-eye);


inline void Matrixd::preMultTranslate( const Vec3d& v )

    for (unsigned i = 0; i < 3; ++i)
    
        double tmp = v[i];
        if (tmp == 0)
            continue;
        _mat[3][0] += tmp*_mat[i][0];
        _mat[3][1] += tmp*_mat[i][1];
        _mat[3][2] += tmp*_mat[i][2];
        _mat[3][3] += tmp*_mat[i][3];
    

希望这会给您一个实施思路。我不太擅长使用可能有更简单解决方案的四元数,但这种方法对我来说效果很好。

【讨论】:

Hhhmmm...所以这会给出一个旋转矩阵,用于将相机指向特定点?为了适应我的问题,我需要一个向量来旋转到否? 这将为相机提供旋转,您需要眼睛位置(向量也是 OSG 中的位置)、中心位置(您的 P),然后是您的向上向量(N)。你说你需要使表面法线实际上是表面法线,我不确定你的意思是什么。你想从法向量上的一点直接看平面的中心吗?如果是这样,请将您的眼睛位置设置为标准化向上的比例。附言其他人都认为这很有趣,这里的每个人都被命名为亚当 :) 是的,它在这里似乎是一个通用名称:P 抱歉,如果我的问题令人困惑,我实际上并不想用我的相机做任何事情。我想旋转我的多边形,以便在其他地方计算的表面法线与多边形的相同(如果我要计算它) 啊,对,我可能只是草草下结论。 Adam Bowen 似乎完全符合您的要求。

以上是关于给定表面法线,找到 3D 平面的旋转的主要内容,如果未能解决你的问题,请参考以下文章

三维重建:三维空间中平面的旋转公式

通过 3D 向量拟合线并找到与平面的交点

沿着两个平面的交点找到线

旋转平面使其具有一定的法向量

想从图像中的二维点找到平面透视世界中的旋转和位置

计算垂直于由点和真北航向描述的平面的 3d 矢量