在OpenGL中沿着折线路径创建管道

3
我想沿着由一系列点定义的路径绘制一个管道。
我的想法是基本上绕着每个点画圆,然后使用GL_QUAD_STRIP将圆上的点连接起来。
因此,我创建圆上的点的代码类似于:
        point_of_circle[0] = pathVertices[i][0] + TUBE_RADIUS * cos(j/TUBE_SEGMENTS * 2*M_PI));
        point_of_circle[1] = pathVertices[i][1] + TUBE_RADIUS * sin(j/TUBE_SEGMENTS * 2*M_PI));
        point_of_circle[2] = pathVertices[i][2];

其中i是路径顶点的索引(即我想要绘制管道的原始点之一),j是正在创建的圆的当前点的索引(它从0到TUBE_SEGMENTS)。

现在问题是,如果我以这种方式创建圆,它们总是朝着同一个方向(圆都是“平行”的,因为我总是执行point_of_circle[2] = pathVertices[i][2]),这不是我想要的,因为结构看起来不像一个合适的管道。

我想到的是计算沿着我正在绘制的路径切线的向量。然后,我需要以某种方式使圆面对根据计算出的切线的正确方向。

例如,如果计算出的切线是0,0,1,则代码应保持不变。但是,如果计算出的切线为其他任何值,则应旋转圆。

例如,如果计算出的切线为0,1,0,则整个圆的y坐标应相同,如果您沿z轴观察,则应仅看到圆的水平段。

如果切线为1,0,0,则如果沿z轴观察,则应看到圆的垂直段。

我该如何编写代码,根据计算出的切线正确创建圆?希望我的解释足够清楚,我是OpenGL和大多数3D数学的初学者。

3个回答

2

我知道那方面的内容,但出于各种自定义的原因,如果我自己动手实现将更为简单。 - houbysoft

1

我已经实现了类似的东西,可以给您一些建议:

设P_0、P1等为路径上的点。管道由线段S_i = (P_i, P_i+1)组成。

您的方法是正确的 - 您可以在P_i周围生成圆形,然后将它们连接起来作为每个线段的四边形带。对于线段S_0到S_N-2,每个线段将有一个圆形,而对于最后一个线段S_N-1,则需要生成两个圆形。

要为线段S_i = (P_i, P_i+1)生成圆形,请计算线段方向D = P_i+1 - P_i。现在,您需要在法线为D的平面上生成一个圆形。这可以用许多方法来完成。也许您有预先计算好的XY平面上的圆形点,您可以对这些点进行变换。

主要问题在于,当你从一个段切换到另一个段时,由于圆上的“起点”不会随着段向量的变化而相匹配,因此管道可能开始扭曲。如果您为管道添加纹理,则这一点尤其重要。解决这个问题的方法之一是根据“顶部标准”选择生成的圆中的第一个点。想象一下三维空间中的一个管道-在视觉上,可以轻松地在管道上画出一条始终保持在管道顶部的三维线。该“顶部”向量取决于管道段方向,并且对于管道的竖直截面没有定义。对于非竖直截面,它由以下公式给出:

T = D X (Z X D)

其中X表示叉积,Z表示朝上方向。

根据我的经验,这很难做对。不幸的是,我无法分享代码。祝好运。


0

在找到切线后,您可以按以下方式获取旋转四元数。 (参考:Ogre3D Vector3.cpp)

    public static Quaternion getRotationTo(Vector3 src, Vector3 dest)
    {
        Quaternion q = new Quaternion();

        src.Normalize();
        dest.Normalize();

        double d = Vector3.Dot(src, dest);

        if (d >= 1.0f)
        {
            return Quaternion.Identity;
        }

        if (d < (1e-6f - 1.0f))
        {
            //                   // Generate an axis
            //                   Vector3 axis = Vector3::UNIT_X.crossProduct(*this);
            //                   if (axis.isZeroLength()) // pick another if colinear
            //                       axis = Vector3::UNIT_Y.crossProduct(*this);
            //                   axis.normalise();
            //                   q.FromAngleAxis(Radian(Math::PI), axis);

            // Generate an axis
            Vector3 axis = Vector3.Cross(Vector3.UnitX, src);

            if (axis.Length() == 0.0f)
            {
                axis = Vector3.Cross(Vector3.UnitY, src);
            }

            axis.Normalize();

            q = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(180));
        }
        else
        {
            //               Real s = Math::Sqrt( (1+d)*2 );
            //               Real invs = 1 / s;

            //               Vector3 c = v0.crossProduct(v1);

            //               q.x = c.x * invs;
            //               q.y = c.y * invs;
            //               q.z = c.z * invs;
            //               q.w = s * 0.5;
            //               q.normalise();

            Double s = Math.Sqrt((1 + d) * 2);
            Double invs = 1 / s;

            Vector3 c = Vector3.Cross(src, dest);

            q.X = (float)(c.X * invs);
            q.Y = (float)(c.Y * invs);
            q.Z = (float)(c.Z * invs);
            q.W = (float)(s * 0.5);
            q.Normalize();

        }


        return q;
    }

这看起来很有前途,但您能否给我一些指针,告诉我如何使用四元数来旋转我的圆?我从未使用过四元数,我查了一下,它看起来像是复数? - houbysoft
https://dev59.com/DXI_5IYBdhLWcg3wBub3 - Sumeet Jindal
我认为我不能使用旋转矩阵,因为代码位于 glBegin()/glEnd() 块之间。有没有办法只使用四元数来获取新坐标呢?就像这样:new_point_of_circle = rotate(point_of_circle, quaternion)?再次感谢并抱歉我是个新手 :) - houbysoft
实际上我找到了这个链接:http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Using_quaternion_rotations,看起来我只需要使用公式 new_point = quat * old_point * quat_conjugate 将旧点乘以四元数得到新点。这样正确吗? - houbysoft
嗯,看起来好像可以了...我会做一些更多的测试,然后可能会在之后写一个完整的答案以备将来参考 :) - houbysoft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接