贝塞尔曲线...添加法线(3D)

Bezier curve... adding normals (in 3D)

我有一条贝塞尔曲线 class。曲线上的每个点都是这样定义的:

struct bpoint
{
    vector3D position;
    vector3D controlpoint_in;
    vector3d controlpoint_out;
};

要检索曲线样条上的一个点(曲线可以有无限个点),我使用这个函数:

vector3d GetSplinePoint(float thePos)
{
   float aPos=thePos-floorf(thePos);
   int aIPos=(int)floorf(thePos);

   bpoint& aP1=pointList[aIPos];
   bpoint& aP2=pointList[min(aIPos+1,pointList.size())];

    vector3D aResult;
    vector3D aAB, aBC, aCD, aABBC, aBCCD;
    Lerp(aAB,aP1.mPos,aP1.mOut,aPos);
    Lerp(aBC,aP1.mOut,aP2.mIn,aPos);
    Lerp(aCD,aP2.mIn,aP2.mPos,aPos);
    Lerp(aABBC,aAB,aBC,aPos);
    Lerp(aBCCD,aBC,aCD,aPos);
    Lerp(aResult,aABBC,aBCCD,aPos);
    return aResult;
}

效果很好,但现在我想为每个点添加法线,这样我就可以做一些更有趣的事情(旋转等)。像这样:

struct bpoint
{
    vector3D position;
    vector3D controlpoint_in;
    vector3d controlpoint_out;
    vector3d normal;
};

我在尝试编写 GetSplineNormal(float thePos) 函数时完全失败了。我什至不确定从哪里开始——我以为我可以在点之间进行插值,但我不知道如何考虑控制点。

我怎样才能做到这一点?

好的,所以,从数学上讲,有几件事需要考虑。

首先,你的曲线有一定的形状,所以在曲线的每一点都有一个且只有一个垂直于曲线当前方向的平面。法线必须是这个平面的一部分。

因此您的曲线由 thePos 参数化,为了简化我的数学符号,我将写为 t:x(t)、y(t)、z(t)。位置 t 处的垂直平面由以下等式给出:

d_t x(t) * (x - x(t)) + d_t y(t) * (y - y(t)) + d_t z( t) * (z - z(t)) = 0,

其中 d_t x(t) 是 x(t) 对 t 的导数,d_t y(t) 对于 y(t) 和 d_t z( t) 对于 z(t).

对于法线向量的方向(即法线在该垂直平面中朝向的方向),您可以在两个控制点之间进行插值。请注意,插值角度可能很棘手,因为您必须考虑方向。角度坐标的线性插值是一种选择,但您也可以直接对法向量进行插值,然后使用最小二乘法重新投影到垂直平面上。这真的取决于你想用法线做什么。

最后,请记住这个垂直平面也存在于控制点,因此您可能必须限制用户给定的法线(通过使用最小二乘法重新投影)。