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

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

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

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

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

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

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

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

【问题讨论】:

    标签: math 3d


    【解决方案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 似乎完全符合您的要求。
    猜你喜欢
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 2013-07-29
    相关资源
    最近更新 更多