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

时间:2010-01-19 19:35:57

标签: math 3d

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

  

P:位于飞机上的一个点   N:平面的表面法线

我有一个非常大的扁平方形多边形,我想渲染它来代表这个平面。我可以很容易地将多边形转换为给定的点,但是我需要找到适当的旋转来使表面法线实际上是表面法线。

我尝试了一个其他方法,其中包括:

  

1)将任何非平行向量(V)取正常值(N),取十字积(W1)
  2)现在取(W1)和(N)的叉积(W2),这是位于平面上的矢量(V')

然后我生成一个基于(V')放置在平面上的旋转矩阵,这样我的多边形就会与(V')对齐。虽然有效,但很明显,这种方法整体上并不正常。多边形与表面法线不完全垂直。

关于如何产生正确旋转的任何想法?

2 个答案:

答案 0 :(得分:14)

关于轮换的一些有用的事情:

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

所以,问题是找到任意一组三个正交向量并将它们排列为

| 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];

这基本上使用Gram-Schmidt正交化,其中维度已经与法向量最正交。然后可以通过取normalv2的叉积来获得v3。

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

我担心我没有测试过上面的内容,我只是从我前一段时间编写的一些代码中删除了它并将其改编为你的问题!希望我没有忘记任何细节。

编辑:我确实忘记了一些事情:)

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

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

答案 1 :(得分: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];
    }
}

希望这会让您对实施有所了解。对于可能有更简单解决方案的四元数我不是很好,但这种方法对我来说效果很好。